mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-12 16:01:27 +08:00
refactor: format code with black
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
- fix #1217: reload debugtalk.py if loaded
|
- fix #1217: reload debugtalk.py if loaded
|
||||||
- fix #1246: catch exceptions caused by GA report failure
|
- fix #1246: catch exceptions caused by GA report failure
|
||||||
|
- refactor: format code with black
|
||||||
|
|
||||||
## 2.5.8 (2022-03-23)
|
## 2.5.8 (2022-03-23)
|
||||||
|
|
||||||
|
|||||||
@@ -37,42 +37,37 @@ token_dict = {}
|
|||||||
|
|
||||||
|
|
||||||
def gen_random_string(str_len):
|
def gen_random_string(str_len):
|
||||||
""" generate random string with specified length
|
"""generate random string with specified length"""
|
||||||
"""
|
return "".join(
|
||||||
return ''.join(
|
random.choice(string.ascii_letters + string.digits) for _ in range(str_len)
|
||||||
random.choice(string.ascii_letters + string.digits) for _ in range(str_len))
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_sign(*args):
|
def get_sign(*args):
|
||||||
content = ''.join(args).encode('ascii')
|
content = "".join(args).encode("ascii")
|
||||||
sign_key = SECRET_KEY.encode('ascii')
|
sign_key = SECRET_KEY.encode("ascii")
|
||||||
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
||||||
return sign
|
return sign
|
||||||
|
|
||||||
|
|
||||||
def gen_md5(*args):
|
def gen_md5(*args):
|
||||||
return hashlib.md5("".join(args).encode('utf-8')).hexdigest()
|
return hashlib.md5("".join(args).encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def validate_request(func):
|
def validate_request(func):
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
device_sn = request.headers.get('device_sn', "")
|
device_sn = request.headers.get("device_sn", "")
|
||||||
token = request.headers.get('token', "")
|
token = request.headers.get("token", "")
|
||||||
|
|
||||||
if not device_sn or not token:
|
if not device_sn or not token:
|
||||||
result = {
|
result = {"success": False, "msg": "device_sn or token is null."}
|
||||||
'success': False,
|
|
||||||
'msg': "device_sn or token is null."
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), 401)
|
response = make_response(json.dumps(result), 401)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
if token_dict.get(device_sn) != token:
|
if token_dict.get(device_sn) != token:
|
||||||
result = {
|
result = {"success": False, "msg": "Authorization failed!"}
|
||||||
'success': False,
|
|
||||||
'msg': "Authorization failed!"
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), 403)
|
response = make_response(json.dumps(result), 403)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
@@ -82,107 +77,89 @@ def validate_request(func):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return "Hello World!"
|
return "Hello World!"
|
||||||
|
|
||||||
@app.route('/api/get-token', methods=['POST'])
|
|
||||||
|
@app.route("/api/get-token", methods=["POST"])
|
||||||
def get_token():
|
def get_token():
|
||||||
device_sn = request.headers.get('device_sn', "")
|
device_sn = request.headers.get("device_sn", "")
|
||||||
os_platform = request.headers.get('os_platform', "")
|
os_platform = request.headers.get("os_platform", "")
|
||||||
app_version = request.headers.get('app_version', "")
|
app_version = request.headers.get("app_version", "")
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
sign = data.get('sign', "")
|
sign = data.get("sign", "")
|
||||||
|
|
||||||
expected_sign = get_sign(device_sn, os_platform, app_version)
|
expected_sign = get_sign(device_sn, os_platform, app_version)
|
||||||
|
|
||||||
if expected_sign != sign:
|
if expected_sign != sign:
|
||||||
result = {
|
result = {"success": False, "msg": "Authorization failed!"}
|
||||||
'success': False,
|
|
||||||
'msg': "Authorization failed!"
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), 403)
|
response = make_response(json.dumps(result), 403)
|
||||||
else:
|
else:
|
||||||
token = gen_random_string(16)
|
token = gen_random_string(16)
|
||||||
token_dict[device_sn] = token
|
token_dict[device_sn] = token
|
||||||
|
|
||||||
result = {
|
result = {"success": True, "token": token}
|
||||||
'success': True,
|
|
||||||
'token': token
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result))
|
response = make_response(json.dumps(result))
|
||||||
|
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/api/users')
|
|
||||||
|
@app.route("/api/users")
|
||||||
@validate_request
|
@validate_request
|
||||||
def get_users():
|
def get_users():
|
||||||
users_list = [user for uid, user in users_dict.items()]
|
users_list = [user for uid, user in users_dict.items()]
|
||||||
users = {
|
users = {"success": True, "count": len(users_list), "items": users_list}
|
||||||
'success': True,
|
|
||||||
'count': len(users_list),
|
|
||||||
'items': users_list
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(users))
|
response = make_response(json.dumps(users))
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/api/reset-all')
|
|
||||||
|
@app.route("/api/reset-all")
|
||||||
@validate_request
|
@validate_request
|
||||||
def clear_users():
|
def clear_users():
|
||||||
users_dict.clear()
|
users_dict.clear()
|
||||||
result = {
|
result = {"success": True}
|
||||||
'success': True
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result))
|
response = make_response(json.dumps(result))
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>', methods=['POST'])
|
|
||||||
|
@app.route("/api/users/<int:uid>", methods=["POST"])
|
||||||
@validate_request
|
@validate_request
|
||||||
def create_user(uid):
|
def create_user(uid):
|
||||||
user = request.get_json()
|
user = request.get_json()
|
||||||
if uid not in users_dict:
|
if uid not in users_dict:
|
||||||
result = {
|
result = {"success": True, "msg": "user created successfully."}
|
||||||
'success': True,
|
|
||||||
'msg': "user created successfully."
|
|
||||||
}
|
|
||||||
status_code = 201
|
status_code = 201
|
||||||
users_dict[uid] = user
|
users_dict[uid] = user
|
||||||
else:
|
else:
|
||||||
result = {
|
result = {"success": False, "msg": "user already existed."}
|
||||||
'success': False,
|
|
||||||
'msg': "user already existed."
|
|
||||||
}
|
|
||||||
status_code = 500
|
status_code = 500
|
||||||
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>')
|
|
||||||
|
@app.route("/api/users/<int:uid>")
|
||||||
@validate_request
|
@validate_request
|
||||||
def get_user(uid):
|
def get_user(uid):
|
||||||
user = users_dict.get(uid, {})
|
user = users_dict.get(uid, {})
|
||||||
if user:
|
if user:
|
||||||
result = {
|
result = {"success": True, "data": user}
|
||||||
'success': True,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
status_code = 200
|
status_code = 200
|
||||||
else:
|
else:
|
||||||
result = {
|
result = {"success": False, "data": user}
|
||||||
'success': False,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
status_code = 404
|
status_code = 404
|
||||||
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>', methods=['PUT'])
|
|
||||||
|
@app.route("/api/users/<int:uid>", methods=["PUT"])
|
||||||
@validate_request
|
@validate_request
|
||||||
def update_user(uid):
|
def update_user(uid):
|
||||||
user = users_dict.get(uid, {})
|
user = users_dict.get(uid, {})
|
||||||
@@ -195,15 +172,13 @@ def update_user(uid):
|
|||||||
success = False
|
success = False
|
||||||
status_code = 404
|
status_code = 404
|
||||||
|
|
||||||
result = {
|
result = {"success": success, "data": user}
|
||||||
'success': success,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>', methods=['DELETE'])
|
|
||||||
|
@app.route("/api/users/<int:uid>", methods=["DELETE"])
|
||||||
@validate_request
|
@validate_request
|
||||||
def delete_user(uid):
|
def delete_user(uid):
|
||||||
user = users_dict.pop(uid, {})
|
user = users_dict.pop(uid, {})
|
||||||
@@ -214,10 +189,7 @@ def delete_user(uid):
|
|||||||
success = False
|
success = False
|
||||||
status_code = 404
|
status_code = 404
|
||||||
|
|
||||||
result = {
|
result = {"success": success, "data": user}
|
||||||
'success': success,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -6,43 +6,41 @@ import time
|
|||||||
|
|
||||||
SECRET_KEY = "DebugTalk"
|
SECRET_KEY = "DebugTalk"
|
||||||
|
|
||||||
|
|
||||||
def gen_random_string(str_len):
|
def gen_random_string(str_len):
|
||||||
random_char_list = []
|
random_char_list = []
|
||||||
for _ in range(str_len):
|
for _ in range(str_len):
|
||||||
random_char = random.choice(string.ascii_letters + string.digits)
|
random_char = random.choice(string.ascii_letters + string.digits)
|
||||||
random_char_list.append(random_char)
|
random_char_list.append(random_char)
|
||||||
|
|
||||||
random_string = ''.join(random_char_list)
|
random_string = "".join(random_char_list)
|
||||||
return random_string
|
return random_string
|
||||||
|
|
||||||
|
|
||||||
def get_sign(*args):
|
def get_sign(*args):
|
||||||
content = ''.join(args).encode('ascii')
|
content = "".join(args).encode("ascii")
|
||||||
sign_key = SECRET_KEY.encode('ascii')
|
sign_key = SECRET_KEY.encode("ascii")
|
||||||
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
||||||
return sign
|
return sign
|
||||||
|
|
||||||
|
|
||||||
def gen_user_id():
|
def gen_user_id():
|
||||||
return int(time.time() * 1000)
|
return int(time.time() * 1000)
|
||||||
|
|
||||||
|
|
||||||
def get_user_id():
|
def get_user_id():
|
||||||
return [
|
return [{"user_id": 1001}, {"user_id": 1002}, {"user_id": 1003}, {"user_id": 1004}]
|
||||||
{"user_id": 1001},
|
|
||||||
{"user_id": 1002},
|
|
||||||
{"user_id": 1003},
|
|
||||||
{"user_id": 1004}
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_account(num):
|
def get_account(num):
|
||||||
accounts = []
|
accounts = []
|
||||||
for index in range(1, num+1):
|
for index in range(1, num + 1):
|
||||||
accounts.append(
|
accounts.append(
|
||||||
{"username": "user%s" % index, "password": str(index) * 6},
|
{"username": "user%s" % index, "password": str(index) * 6},
|
||||||
)
|
)
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
|
|
||||||
def get_os_platform():
|
def get_os_platform():
|
||||||
return [
|
return [{"os_platform": "ios"}, {"os_platform": "android"}]
|
||||||
{"os_platform": "ios"},
|
|
||||||
{"os_platform": "android"}
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ __all__ = [
|
|||||||
klook_len_eq,
|
klook_len_eq,
|
||||||
check_search_area_result,
|
check_search_area_result,
|
||||||
exists_default_group,
|
exists_default_group,
|
||||||
teardown_hook_set_encoding
|
teardown_hook_set_encoding,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ def check_search_area_result(content, expect_name):
|
|||||||
print(content, expect_name)
|
print(content, expect_name)
|
||||||
found = False
|
found = False
|
||||||
for item in content:
|
for item in content:
|
||||||
if item['fullName'] == expect_name:
|
if item["fullName"] == expect_name:
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
assert found
|
assert found
|
||||||
@@ -11,8 +11,12 @@ def check_search_area_result(content, expect_name):
|
|||||||
def exists_default_group(content, expect):
|
def exists_default_group(content, expect):
|
||||||
found = False
|
found = False
|
||||||
for item in content:
|
for item in content:
|
||||||
if item['defaultGroup']:
|
if item["defaultGroup"]:
|
||||||
print('defaultGroup found, id={}, parentAreaId={}'.format(item['id'], item['parentAreaId']))
|
print(
|
||||||
|
"defaultGroup found, id={}, parentAreaId={}".format(
|
||||||
|
item["id"], item["parentAreaId"]
|
||||||
|
)
|
||||||
|
)
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
assert found
|
assert found
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from httprunner import (__version__, exceptions, loader, logger, parser,
|
from httprunner import (
|
||||||
report, runner, utils)
|
__version__,
|
||||||
|
exceptions,
|
||||||
|
loader,
|
||||||
|
logger,
|
||||||
|
parser,
|
||||||
|
report,
|
||||||
|
runner,
|
||||||
|
utils,
|
||||||
|
)
|
||||||
from httprunner.utils import ga_client
|
from httprunner.utils import ga_client
|
||||||
|
|
||||||
|
|
||||||
class HttpRunner(object):
|
class HttpRunner(object):
|
||||||
""" Developer Interface: Main Interface
|
"""Developer Interface: Main Interface
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
from httprunner.api import HttpRunner
|
from httprunner.api import HttpRunner
|
||||||
runner = HttpRunner(
|
runner = HttpRunner(
|
||||||
failfast=True,
|
failfast=True,
|
||||||
save_tests=True,
|
save_tests=True,
|
||||||
log_level="INFO",
|
log_level="INFO",
|
||||||
log_file="test.log"
|
log_file="test.log"
|
||||||
)
|
)
|
||||||
summary = runner.run(path_or_tests)
|
summary = runner.run(path_or_tests)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, failfast=False, save_tests=False, log_level="WARNING", log_file=None):
|
def __init__(
|
||||||
""" initialize HttpRunner.
|
self, failfast=False, save_tests=False, log_level="WARNING", log_file=None
|
||||||
|
):
|
||||||
|
"""initialize HttpRunner.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
failfast (bool): stop the test run on the first error or failure.
|
failfast (bool): stop the test run on the first error or failure.
|
||||||
@@ -34,10 +44,7 @@ class HttpRunner(object):
|
|||||||
logger.setup_logger(log_level, log_file)
|
logger.setup_logger(log_level, log_file)
|
||||||
|
|
||||||
self.exception_stage = "initialize HttpRunner()"
|
self.exception_stage = "initialize HttpRunner()"
|
||||||
kwargs = {
|
kwargs = {"failfast": failfast, "resultclass": report.HtmlTestResult}
|
||||||
"failfast": failfast,
|
|
||||||
"resultclass": report.HtmlTestResult
|
|
||||||
}
|
|
||||||
self.unittest_runner = unittest.TextTestRunner(**kwargs)
|
self.unittest_runner = unittest.TextTestRunner(**kwargs)
|
||||||
self.test_loader = unittest.TestLoader()
|
self.test_loader = unittest.TestLoader()
|
||||||
self.save_tests = save_tests
|
self.save_tests = save_tests
|
||||||
@@ -45,7 +52,7 @@ class HttpRunner(object):
|
|||||||
self.project_working_directory = None
|
self.project_working_directory = None
|
||||||
|
|
||||||
def _add_tests(self, testcases):
|
def _add_tests(self, testcases):
|
||||||
""" initialize testcase with Runner() and add to test suite.
|
"""initialize testcase with Runner() and add to test suite.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
testcases (list): testcases list.
|
testcases (list): testcases list.
|
||||||
@@ -54,9 +61,10 @@ class HttpRunner(object):
|
|||||||
unittest.TestSuite()
|
unittest.TestSuite()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _add_test(test_runner, test_dict):
|
def _add_test(test_runner, test_dict):
|
||||||
""" add test to testcase.
|
"""add test to testcase."""
|
||||||
"""
|
|
||||||
def test(self):
|
def test(self):
|
||||||
try:
|
try:
|
||||||
test_runner.run_test(test_dict)
|
test_runner.run_test(test_dict)
|
||||||
@@ -89,7 +97,7 @@ class HttpRunner(object):
|
|||||||
for testcase in testcases:
|
for testcase in testcases:
|
||||||
config = testcase.get("config", {})
|
config = testcase.get("config", {})
|
||||||
test_runner = runner.Runner(config)
|
test_runner = runner.Runner(config)
|
||||||
TestSequense = type('TestSequense', (unittest.TestCase,), {})
|
TestSequense = type("TestSequense", (unittest.TestCase,), {})
|
||||||
|
|
||||||
tests = testcase.get("teststeps", [])
|
tests = testcase.get("teststeps", [])
|
||||||
for index, test_dict in enumerate(tests):
|
for index, test_dict in enumerate(tests):
|
||||||
@@ -98,12 +106,13 @@ class HttpRunner(object):
|
|||||||
times = int(times)
|
times = int(times)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise exceptions.ParamsError(
|
raise exceptions.ParamsError(
|
||||||
"times should be digit, given: {}".format(times))
|
"times should be digit, given: {}".format(times)
|
||||||
|
)
|
||||||
|
|
||||||
for times_index in range(times):
|
for times_index in range(times):
|
||||||
# suppose one testcase should not have more than 9999 steps,
|
# suppose one testcase should not have more than 9999 steps,
|
||||||
# and one step should not run more than 999 times.
|
# and one step should not run more than 999 times.
|
||||||
test_method_name = 'test_{:04}_{:03}'.format(index, times_index)
|
test_method_name = "test_{:04}_{:03}".format(index, times_index)
|
||||||
test_method = _add_test(test_runner, test_dict)
|
test_method = _add_test(test_runner, test_dict)
|
||||||
setattr(TestSequense, test_method_name, test_method)
|
setattr(TestSequense, test_method_name, test_method)
|
||||||
|
|
||||||
@@ -116,7 +125,7 @@ class HttpRunner(object):
|
|||||||
return test_suite
|
return test_suite
|
||||||
|
|
||||||
def _run_suite(self, test_suite):
|
def _run_suite(self, test_suite):
|
||||||
""" run tests in test_suite
|
"""run tests in test_suite
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
test_suite: unittest.TestSuite()
|
test_suite: unittest.TestSuite()
|
||||||
@@ -140,7 +149,7 @@ class HttpRunner(object):
|
|||||||
return tests_results
|
return tests_results
|
||||||
|
|
||||||
def _aggregate(self, tests_results):
|
def _aggregate(self, tests_results):
|
||||||
""" aggregate results
|
"""aggregate results
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tests_results (list): list of (testcase, result)
|
tests_results (list): list of (testcase, result)
|
||||||
@@ -149,16 +158,12 @@ class HttpRunner(object):
|
|||||||
summary = {
|
summary = {
|
||||||
"success": True,
|
"success": True,
|
||||||
"stat": {
|
"stat": {
|
||||||
"testcases": {
|
"testcases": {"total": len(tests_results), "success": 0, "fail": 0},
|
||||||
"total": len(tests_results),
|
"teststeps": {},
|
||||||
"success": 0,
|
|
||||||
"fail": 0
|
|
||||||
},
|
|
||||||
"teststeps": {}
|
|
||||||
},
|
},
|
||||||
"time": {},
|
"time": {},
|
||||||
"platform": report.get_platform(),
|
"platform": report.get_platform(),
|
||||||
"details": []
|
"details": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
for tests_result in tests_results:
|
for tests_result in tests_results:
|
||||||
@@ -174,7 +179,9 @@ class HttpRunner(object):
|
|||||||
testcase_summary["name"] = testcase.config.get("name")
|
testcase_summary["name"] = testcase.config.get("name")
|
||||||
testcase_summary["in_out"] = utils.get_testcase_io(testcase)
|
testcase_summary["in_out"] = utils.get_testcase_io(testcase)
|
||||||
|
|
||||||
report.aggregate_stat(summary["stat"]["teststeps"], testcase_summary["stat"])
|
report.aggregate_stat(
|
||||||
|
summary["stat"]["teststeps"], testcase_summary["stat"]
|
||||||
|
)
|
||||||
report.aggregate_stat(summary["time"], testcase_summary["time"])
|
report.aggregate_stat(summary["time"], testcase_summary["time"])
|
||||||
|
|
||||||
summary["details"].append(testcase_summary)
|
summary["details"].append(testcase_summary)
|
||||||
@@ -182,8 +189,7 @@ class HttpRunner(object):
|
|||||||
return summary
|
return summary
|
||||||
|
|
||||||
def run_tests(self, tests_mapping):
|
def run_tests(self, tests_mapping):
|
||||||
""" run testcase/testsuite data
|
"""run testcase/testsuite data"""
|
||||||
"""
|
|
||||||
ga_client.track_event("RunAPITests", "hrun")
|
ga_client.track_event("RunAPITests", "hrun")
|
||||||
project_mapping = tests_mapping.get("project_mapping", {})
|
project_mapping = tests_mapping.get("project_mapping", {})
|
||||||
self.project_working_directory = project_mapping.get("PWD", os.getcwd())
|
self.project_working_directory = project_mapping.get("PWD", os.getcwd())
|
||||||
@@ -231,7 +237,7 @@ class HttpRunner(object):
|
|||||||
return self._summary
|
return self._summary
|
||||||
|
|
||||||
def get_vars_out(self):
|
def get_vars_out(self):
|
||||||
""" get variables and output
|
"""get variables and output
|
||||||
Returns:
|
Returns:
|
||||||
list: list of variables and output.
|
list: list of variables and output.
|
||||||
if tests are parameterized, list items are corresponded to parameters.
|
if tests are parameterized, list items are corresponded to parameters.
|
||||||
@@ -254,13 +260,10 @@ class HttpRunner(object):
|
|||||||
if not self._summary:
|
if not self._summary:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return [
|
return [summary["in_out"] for summary in self._summary["details"]]
|
||||||
summary["in_out"]
|
|
||||||
for summary in self._summary["details"]
|
|
||||||
]
|
|
||||||
|
|
||||||
def run_path(self, path, dot_env_path=None, mapping=None):
|
def run_path(self, path, dot_env_path=None, mapping=None):
|
||||||
""" run testcase/testsuite file or folder.
|
"""run testcase/testsuite file or folder.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str): testcase/testsuite file/foler path.
|
path (str): testcase/testsuite file/foler path.
|
||||||
@@ -281,7 +284,7 @@ class HttpRunner(object):
|
|||||||
return self.run_tests(tests_mapping)
|
return self.run_tests(tests_mapping)
|
||||||
|
|
||||||
def run(self, path_or_tests, dot_env_path=None, mapping=None):
|
def run(self, path_or_tests, dot_env_path=None, mapping=None):
|
||||||
""" main interface.
|
"""main interface.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path_or_tests:
|
path_or_tests:
|
||||||
@@ -298,8 +301,12 @@ class HttpRunner(object):
|
|||||||
if loader.is_test_path(path_or_tests):
|
if loader.is_test_path(path_or_tests):
|
||||||
return self.run_path(path_or_tests, dot_env_path, mapping)
|
return self.run_path(path_or_tests, dot_env_path, mapping)
|
||||||
elif loader.is_test_content(path_or_tests):
|
elif loader.is_test_content(path_or_tests):
|
||||||
project_working_directory = path_or_tests.get("project_mapping", {}).get("PWD", os.getcwd())
|
project_working_directory = path_or_tests.get("project_mapping", {}).get(
|
||||||
|
"PWD", os.getcwd()
|
||||||
|
)
|
||||||
loader.init_pwd(project_working_directory)
|
loader.init_pwd(project_working_directory)
|
||||||
return self.run_tests(path_or_tests)
|
return self.run_tests(path_or_tests)
|
||||||
else:
|
else:
|
||||||
raise exceptions.ParamsError("Invalid testcase path or testcases: {}".format(path_or_tests))
|
raise exceptions.ParamsError(
|
||||||
|
"Invalid testcase path or testcases: {}".format(path_or_tests)
|
||||||
|
)
|
||||||
|
|||||||
@@ -108,4 +108,4 @@ def _cast_to_int(expect_value):
|
|||||||
try:
|
try:
|
||||||
return int(expect_value)
|
return int(expect_value)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise AssertionError("%r can't cast to int" % str(expect_value))
|
raise AssertionError("%r can't cast to int" % str(expect_value))
|
||||||
|
|||||||
@@ -12,15 +12,14 @@ from httprunner.exceptions import ParamsError
|
|||||||
|
|
||||||
|
|
||||||
def gen_random_string(str_len):
|
def gen_random_string(str_len):
|
||||||
""" generate random string with specified length
|
"""generate random string with specified length"""
|
||||||
"""
|
return "".join(
|
||||||
return ''.join(
|
random.choice(string.ascii_letters + string.digits) for _ in range(str_len)
|
||||||
random.choice(string.ascii_letters + string.digits) for _ in range(str_len))
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_timestamp(str_len=13):
|
def get_timestamp(str_len=13):
|
||||||
""" get timestamp string, length can only between 0 and 16
|
"""get timestamp string, length can only between 0 and 16"""
|
||||||
"""
|
|
||||||
if isinstance(str_len, integer_types) and 0 < str_len < 17:
|
if isinstance(str_len, integer_types) and 0 < str_len < 17:
|
||||||
return builtin_str(time.time()).replace(".", "")[:str_len]
|
return builtin_str(time.time()).replace(".", "")[:str_len]
|
||||||
|
|
||||||
@@ -28,13 +27,10 @@ def get_timestamp(str_len=13):
|
|||||||
|
|
||||||
|
|
||||||
def get_current_date(fmt="%Y-%m-%d"):
|
def get_current_date(fmt="%Y-%m-%d"):
|
||||||
""" get current date, default format is %Y-%m-%d
|
"""get current date, default format is %Y-%m-%d"""
|
||||||
"""
|
|
||||||
return datetime.datetime.now().strftime(fmt)
|
return datetime.datetime.now().strftime(fmt)
|
||||||
|
|
||||||
|
|
||||||
def sleep(n_secs):
|
def sleep(n_secs):
|
||||||
""" sleep n seconds
|
"""sleep n seconds"""
|
||||||
"""
|
|
||||||
time.sleep(n_secs)
|
time.sleep(n_secs)
|
||||||
|
|
||||||
|
|||||||
@@ -10,58 +10,63 @@ from httprunner.compat import is_py2
|
|||||||
from httprunner.loader import load_cases
|
from httprunner.loader import load_cases
|
||||||
from httprunner.logger import color_print, log_error
|
from httprunner.logger import color_print, log_error
|
||||||
from httprunner.report import gen_html_report
|
from httprunner.report import gen_html_report
|
||||||
from httprunner.utils import (create_scaffold, get_python2_retire_msg,
|
from httprunner.utils import (
|
||||||
prettify_json_file, init_sentry_sdk)
|
create_scaffold,
|
||||||
|
get_python2_retire_msg,
|
||||||
|
prettify_json_file,
|
||||||
|
init_sentry_sdk,
|
||||||
|
)
|
||||||
|
|
||||||
init_sentry_sdk()
|
init_sentry_sdk()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" API test: parse command line options and run commands.
|
"""API test: parse command line options and run commands."""
|
||||||
"""
|
|
||||||
if is_py2:
|
if is_py2:
|
||||||
color_print(get_python2_retire_msg(), "YELLOW")
|
color_print(get_python2_retire_msg(), "YELLOW")
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=__description__)
|
parser = argparse.ArgumentParser(description=__description__)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-V', '--version', dest='version', action='store_true',
|
"-V", "--version", dest="version", action="store_true", help="show version"
|
||||||
help="show version")
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'testfile_paths', nargs='*',
|
"testfile_paths",
|
||||||
help="Specify api/testcase/testsuite file paths to run.")
|
nargs="*",
|
||||||
|
help="Specify api/testcase/testsuite file paths to run.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--log-level', default='INFO',
|
"--log-level", default="INFO", help="Specify logging level, default is INFO."
|
||||||
help="Specify logging level, default is INFO.")
|
)
|
||||||
|
parser.add_argument("--log-file", help="Write logs to specified file path.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--log-file',
|
"--dot-env-path",
|
||||||
help="Write logs to specified file path.")
|
help="Specify .env file path, which is useful for keeping sensitive data.",
|
||||||
|
)
|
||||||
|
parser.add_argument("--report-template", help="Specify report template path.")
|
||||||
|
parser.add_argument("--report-dir", help="Specify report save directory.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--dot-env-path',
|
"--report-file",
|
||||||
help="Specify .env file path, which is useful for keeping sensitive data.")
|
help="Specify report file path, this has higher priority than specifying report dir.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--report-template',
|
"--save-tests",
|
||||||
help="Specify report template path.")
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Save loaded/parsed/vars_out/summary json data to JSON files.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--report-dir',
|
"--failfast",
|
||||||
help="Specify report save directory.")
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Stop the test run on the first error or failure.",
|
||||||
|
)
|
||||||
|
parser.add_argument("--startproject", help="Specify new project name.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--report-file',
|
"--validate",
|
||||||
help="Specify report file path, this has higher priority than specifying report dir.")
|
nargs="*",
|
||||||
parser.add_argument(
|
help="Validate YAML/JSON api/testcase/testsuite format.",
|
||||||
'--save-tests', action='store_true', default=False,
|
)
|
||||||
help="Save loaded/parsed/vars_out/summary json data to JSON files.")
|
parser.add_argument("--prettify", nargs="*", help="Prettify JSON testcase format.")
|
||||||
parser.add_argument(
|
|
||||||
'--failfast', action='store_true', default=False,
|
|
||||||
help="Stop the test run on the first error or failure.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--startproject',
|
|
||||||
help="Specify new project name.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--validate', nargs='*',
|
|
||||||
help="Validate YAML/JSON api/testcase/testsuite format.")
|
|
||||||
parser.add_argument(
|
|
||||||
'--prettify', nargs='*',
|
|
||||||
help="Prettify JSON testcase format.")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -99,23 +104,28 @@ def main():
|
|||||||
failfast=args.failfast,
|
failfast=args.failfast,
|
||||||
save_tests=args.save_tests,
|
save_tests=args.save_tests,
|
||||||
log_level=args.log_level,
|
log_level=args.log_level,
|
||||||
log_file=args.log_file
|
log_file=args.log_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
err_code = 0
|
err_code = 0
|
||||||
try:
|
try:
|
||||||
for path in args.testfile_paths:
|
for path in args.testfile_paths:
|
||||||
summary = runner.run(path, dot_env_path=args.dot_env_path)
|
summary = runner.run(path, dot_env_path=args.dot_env_path)
|
||||||
report_dir = args.report_dir or os.path.join(runner.project_working_directory, "reports")
|
report_dir = args.report_dir or os.path.join(
|
||||||
|
runner.project_working_directory, "reports"
|
||||||
|
)
|
||||||
gen_html_report(
|
gen_html_report(
|
||||||
summary,
|
summary,
|
||||||
report_template=args.report_template,
|
report_template=args.report_template,
|
||||||
report_dir=report_dir,
|
report_dir=report_dir,
|
||||||
report_file=args.report_file
|
report_file=args.report_file,
|
||||||
)
|
)
|
||||||
err_code |= (0 if summary and summary["success"] else 1)
|
err_code |= 0 if summary and summary["success"] else 1
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
color_print("!!!!!!!!!! exception stage: {} !!!!!!!!!!".format(runner.exception_stage), "YELLOW")
|
color_print(
|
||||||
|
"!!!!!!!!!! exception stage: {} !!!!!!!!!!".format(runner.exception_stage),
|
||||||
|
"YELLOW",
|
||||||
|
)
|
||||||
color_print(str(ex), "RED")
|
color_print(str(ex), "RED")
|
||||||
sentry_sdk.capture_exception(ex)
|
sentry_sdk.capture_exception(ex)
|
||||||
err_code = 1
|
err_code = 1
|
||||||
@@ -123,5 +133,5 @@ def main():
|
|||||||
sys.exit(err_code)
|
sys.exit(err_code)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ import time
|
|||||||
import requests
|
import requests
|
||||||
import urllib3
|
import urllib3
|
||||||
from requests import Request, Response
|
from requests import Request, Response
|
||||||
from requests.exceptions import (InvalidSchema, InvalidURL, MissingSchema,
|
from requests.exceptions import (
|
||||||
RequestException)
|
InvalidSchema,
|
||||||
|
InvalidURL,
|
||||||
|
MissingSchema,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
|
||||||
from httprunner import logger, response
|
from httprunner import logger, response
|
||||||
from httprunner.utils import lower_dict_keys, omit_long_data
|
from httprunner.utils import lower_dict_keys, omit_long_data
|
||||||
@@ -15,18 +19,15 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|||||||
|
|
||||||
|
|
||||||
def get_req_resp_record(resp_obj):
|
def get_req_resp_record(resp_obj):
|
||||||
""" get request and response info from Response() object.
|
"""get request and response info from Response() object."""
|
||||||
"""
|
|
||||||
def log_print(req_resp_dict, r_type):
|
def log_print(req_resp_dict, r_type):
|
||||||
msg = "\n================== {} details ==================\n".format(r_type)
|
msg = "\n================== {} details ==================\n".format(r_type)
|
||||||
for key, value in req_resp_dict[r_type].items():
|
for key, value in req_resp_dict[r_type].items():
|
||||||
msg += "{:<16} : {}\n".format(key, repr(value))
|
msg += "{:<16} : {}\n".format(key, repr(value))
|
||||||
logger.log_debug(msg)
|
logger.log_debug(msg)
|
||||||
|
|
||||||
req_resp_dict = {
|
req_resp_dict = {"request": {}, "response": {}}
|
||||||
"request": {},
|
|
||||||
"response": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
# record actual request info
|
# record actual request info
|
||||||
req_resp_dict["request"]["url"] = resp_obj.request.url
|
req_resp_dict["request"]["url"] = resp_obj.request.url
|
||||||
@@ -35,9 +36,9 @@ def get_req_resp_record(resp_obj):
|
|||||||
|
|
||||||
request_body = resp_obj.request.body
|
request_body = resp_obj.request.body
|
||||||
if request_body:
|
if request_body:
|
||||||
request_content_type = lower_dict_keys(
|
request_content_type = lower_dict_keys(req_resp_dict["request"]["headers"]).get(
|
||||||
req_resp_dict["request"]["headers"]
|
"content-type"
|
||||||
).get("content-type")
|
)
|
||||||
if request_content_type and "multipart/form-data" in request_content_type:
|
if request_content_type and "multipart/form-data" in request_content_type:
|
||||||
# upload file type
|
# upload file type
|
||||||
req_resp_dict["request"]["body"] = "upload file stream (OMITTED)"
|
req_resp_dict["request"]["body"] = "upload file stream (OMITTED)"
|
||||||
@@ -83,9 +84,8 @@ def get_req_resp_record(resp_obj):
|
|||||||
|
|
||||||
|
|
||||||
class ApiResponse(Response):
|
class ApiResponse(Response):
|
||||||
|
|
||||||
def raise_for_status(self):
|
def raise_for_status(self):
|
||||||
if hasattr(self, 'error') and self.error:
|
if hasattr(self, "error") and self.error:
|
||||||
raise self.error
|
raise self.error
|
||||||
Response.raise_for_status(self)
|
Response.raise_for_status(self)
|
||||||
|
|
||||||
@@ -99,35 +99,31 @@ class HttpSession(requests.Session):
|
|||||||
This is a slightly extended version of `python-request <http://python-requests.org>`_'s
|
This is a slightly extended version of `python-request <http://python-requests.org>`_'s
|
||||||
:py:class:`requests.Session` class and mostly this class works exactly the same.
|
:py:class:`requests.Session` class and mostly this class works exactly the same.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(HttpSession, self).__init__()
|
super(HttpSession, self).__init__()
|
||||||
self.init_meta_data()
|
self.init_meta_data()
|
||||||
|
|
||||||
def init_meta_data(self):
|
def init_meta_data(self):
|
||||||
""" initialize meta_data, it will store detail data of request and response
|
"""initialize meta_data, it will store detail data of request and response"""
|
||||||
"""
|
|
||||||
self.meta_data = {
|
self.meta_data = {
|
||||||
"name": "",
|
"name": "",
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"request": {
|
"request": {"url": "N/A", "method": "N/A", "headers": {}},
|
||||||
"url": "N/A",
|
|
||||||
"method": "N/A",
|
|
||||||
"headers": {}
|
|
||||||
},
|
|
||||||
"response": {
|
"response": {
|
||||||
"status_code": "N/A",
|
"status_code": "N/A",
|
||||||
"headers": {},
|
"headers": {},
|
||||||
"encoding": None,
|
"encoding": None,
|
||||||
"content_type": ""
|
"content_type": "",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stat": {
|
"stat": {
|
||||||
"content_size": "N/A",
|
"content_size": "N/A",
|
||||||
"response_time_ms": "N/A",
|
"response_time_ms": "N/A",
|
||||||
"elapsed_ms": "N/A",
|
"elapsed_ms": "N/A",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def update_last_req_resp_record(self, resp_obj):
|
def update_last_req_resp_record(self, resp_obj):
|
||||||
@@ -202,26 +198,23 @@ class HttpSession(requests.Session):
|
|||||||
self.meta_data["stat"] = {
|
self.meta_data["stat"] = {
|
||||||
"response_time_ms": response_time_ms,
|
"response_time_ms": response_time_ms,
|
||||||
"elapsed_ms": response.elapsed.microseconds / 1000.0,
|
"elapsed_ms": response.elapsed.microseconds / 1000.0,
|
||||||
"content_size": content_size
|
"content_size": content_size,
|
||||||
}
|
}
|
||||||
|
|
||||||
# record request and response histories, include 30X redirection
|
# record request and response histories, include 30X redirection
|
||||||
response_list = response.history + [response]
|
response_list = response.history + [response]
|
||||||
self.meta_data["data"] = [
|
self.meta_data["data"] = [
|
||||||
get_req_resp_record(resp_obj)
|
get_req_resp_record(resp_obj) for resp_obj in response_list
|
||||||
for resp_obj in response_list
|
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except RequestException as e:
|
except RequestException as e:
|
||||||
logger.log_error(u"{exception}".format(exception=str(e)))
|
logger.log_error("{exception}".format(exception=str(e)))
|
||||||
else:
|
else:
|
||||||
logger.log_info(
|
logger.log_info(
|
||||||
"""status_code: {}, response_time(ms): {} ms, response_length: {} bytes\n""".format(
|
"""status_code: {}, response_time(ms): {} ms, response_length: {} bytes\n""".format(
|
||||||
response.status_code,
|
response.status_code, response_time_ms, content_size
|
||||||
response_time_ms,
|
|
||||||
content_size
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ import sys
|
|||||||
_ver = sys.version_info
|
_ver = sys.version_info
|
||||||
|
|
||||||
#: Python 2.x?
|
#: Python 2.x?
|
||||||
is_py2 = (_ver[0] == 2)
|
is_py2 = _ver[0] == 2
|
||||||
|
|
||||||
#: Python 3.x?
|
#: Python 3.x?
|
||||||
is_py3 = (_ver[0] == 3)
|
is_py3 = _ver[0] == 3
|
||||||
|
|
||||||
|
|
||||||
# ---------
|
# ---------
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from httprunner import parser, utils
|
|||||||
|
|
||||||
|
|
||||||
class SessionContext(object):
|
class SessionContext(object):
|
||||||
""" HttpRunner session, store runtime variables.
|
"""HttpRunner session, store runtime variables.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
>>> variables = {"SECRET_KEY": "DebugTalk"}
|
>>> variables = {"SECRET_KEY": "DebugTalk"}
|
||||||
@@ -16,12 +16,14 @@ class SessionContext(object):
|
|||||||
|
|
||||||
def __init__(self, variables=None):
|
def __init__(self, variables=None):
|
||||||
variables_mapping = utils.ensure_mapping_format(variables or {})
|
variables_mapping = utils.ensure_mapping_format(variables or {})
|
||||||
self.session_variables_mapping = parser.parse_variables_mapping(variables_mapping)
|
self.session_variables_mapping = parser.parse_variables_mapping(
|
||||||
|
variables_mapping
|
||||||
|
)
|
||||||
self.test_variables_mapping = {}
|
self.test_variables_mapping = {}
|
||||||
self.init_test_variables()
|
self.init_test_variables()
|
||||||
|
|
||||||
def init_test_variables(self, variables_mapping=None):
|
def init_test_variables(self, variables_mapping=None):
|
||||||
""" init test variables, called when each test(api) starts.
|
"""init test variables, called when each test(api) starts.
|
||||||
variables_mapping will be evaluated first.
|
variables_mapping will be evaluated first.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -45,20 +47,19 @@ class SessionContext(object):
|
|||||||
self.test_variables_mapping.update(self.session_variables_mapping)
|
self.test_variables_mapping.update(self.session_variables_mapping)
|
||||||
|
|
||||||
def update_test_variables(self, variable_name, variable_value):
|
def update_test_variables(self, variable_name, variable_value):
|
||||||
""" update test variables, these variables are only valid in the current test.
|
"""update test variables, these variables are only valid in the current test."""
|
||||||
"""
|
|
||||||
self.test_variables_mapping[variable_name] = variable_value
|
self.test_variables_mapping[variable_name] = variable_value
|
||||||
|
|
||||||
def update_session_variables(self, variables_mapping):
|
def update_session_variables(self, variables_mapping):
|
||||||
""" update session with extracted variables mapping.
|
"""update session with extracted variables mapping.
|
||||||
these variables are valid in the whole running session.
|
these variables are valid in the whole running session.
|
||||||
"""
|
"""
|
||||||
variables_mapping = utils.ensure_mapping_format(variables_mapping)
|
variables_mapping = utils.ensure_mapping_format(variables_mapping)
|
||||||
self.session_variables_mapping.update(variables_mapping)
|
self.session_variables_mapping.update(variables_mapping)
|
||||||
self.test_variables_mapping.update(self.session_variables_mapping)
|
self.test_variables_mapping.update(self.session_variables_mapping)
|
||||||
|
|
||||||
def eval_content(self, content):
|
def eval_content(self, content):
|
||||||
""" evaluate content recursively, take effect on each variable and function in content.
|
"""evaluate content recursively, take effect on each variable and function in content.
|
||||||
content may be in any data structure, include dict, list, tuple, number, string, etc.
|
content may be in any data structure, include dict, list, tuple, number, string, etc.
|
||||||
"""
|
"""
|
||||||
return parser.parse_lazy_data(content, self.test_variables_mapping)
|
return parser.parse_lazy_data(content, self.test_variables_mapping)
|
||||||
|
|||||||
@@ -81,5 +81,4 @@ class TestcaseNotFound(NotFoundError):
|
|||||||
|
|
||||||
|
|
||||||
class SummaryEmpty(MyBaseError):
|
class SummaryEmpty(MyBaseError):
|
||||||
""" test result summary data is empty
|
"""test result summary data is empty"""
|
||||||
"""
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
try:
|
try:
|
||||||
# monkey patch ssl at beginning to avoid RecursionError when running locust.
|
# monkey patch ssl at beginning to avoid RecursionError when running locust.
|
||||||
from gevent import monkey
|
from gevent import monkey
|
||||||
|
|
||||||
monkey.patch_ssl()
|
monkey.patch_ssl()
|
||||||
from locust import main as locust_main
|
from locust import main as locust_main
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -11,6 +12,7 @@ $ pip install locustio
|
|||||||
"""
|
"""
|
||||||
print(msg)
|
print(msg)
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
import io
|
import io
|
||||||
@@ -26,9 +28,9 @@ init_sentry_sdk()
|
|||||||
|
|
||||||
|
|
||||||
def parse_locustfile(file_path):
|
def parse_locustfile(file_path):
|
||||||
""" parse testcase file and return locustfile path.
|
"""parse testcase file and return locustfile path.
|
||||||
if file_path is a Python file, assume it is a locustfile
|
if file_path is a Python file, assume it is a locustfile
|
||||||
if file_path is a YAML/JSON file, convert it to locustfile
|
if file_path is a YAML/JSON file, convert it to locustfile
|
||||||
"""
|
"""
|
||||||
if not os.path.isfile(file_path):
|
if not os.path.isfile(file_path):
|
||||||
logger.color_print("file path invalid, exit.", "RED")
|
logger.color_print("file path invalid, exit.", "RED")
|
||||||
@@ -37,7 +39,7 @@ def parse_locustfile(file_path):
|
|||||||
file_suffix = os.path.splitext(file_path)[1]
|
file_suffix = os.path.splitext(file_path)[1]
|
||||||
if file_suffix == ".py":
|
if file_suffix == ".py":
|
||||||
locustfile_path = file_path
|
locustfile_path = file_path
|
||||||
elif file_suffix in ['.yaml', '.yml', '.json']:
|
elif file_suffix in [".yaml", ".yml", ".json"]:
|
||||||
locustfile_path = gen_locustfile(file_path)
|
locustfile_path = gen_locustfile(file_path)
|
||||||
else:
|
else:
|
||||||
# '' or other suffix
|
# '' or other suffix
|
||||||
@@ -48,18 +50,18 @@ def parse_locustfile(file_path):
|
|||||||
|
|
||||||
|
|
||||||
def gen_locustfile(testcase_file_path):
|
def gen_locustfile(testcase_file_path):
|
||||||
""" generate locustfile from template.
|
"""generate locustfile from template."""
|
||||||
"""
|
locustfile_path = "locustfile.py"
|
||||||
locustfile_path = 'locustfile.py'
|
|
||||||
template_path = os.path.join(
|
template_path = os.path.join(
|
||||||
os.path.dirname(os.path.realpath(__file__)),
|
os.path.dirname(os.path.realpath(__file__)), "locustfile_template.py"
|
||||||
"locustfile_template.py"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with io.open(template_path, encoding='utf-8') as template:
|
with io.open(template_path, encoding="utf-8") as template:
|
||||||
with io.open(locustfile_path, 'w', encoding='utf-8') as locustfile:
|
with io.open(locustfile_path, "w", encoding="utf-8") as locustfile:
|
||||||
template_content = template.read()
|
template_content = template.read()
|
||||||
template_content = template_content.replace("$TESTCASE_FILE", testcase_file_path)
|
template_content = template_content.replace(
|
||||||
|
"$TESTCASE_FILE", testcase_file_path
|
||||||
|
)
|
||||||
locustfile.write(template_content)
|
locustfile.write(template_content)
|
||||||
|
|
||||||
return locustfile_path
|
return locustfile_path
|
||||||
@@ -67,6 +69,7 @@ def gen_locustfile(testcase_file_path):
|
|||||||
|
|
||||||
def start_locust_main():
|
def start_locust_main():
|
||||||
from httprunner.utils import ga_client
|
from httprunner.utils import ga_client
|
||||||
|
|
||||||
ga_client.track_event("RunLoadTests", "locust")
|
ga_client.track_event("RunLoadTests", "locust")
|
||||||
|
|
||||||
locust_main.main()
|
locust_main.main()
|
||||||
@@ -106,10 +109,9 @@ def run_locusts_with_processes(sys_argv, processes_count):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Performance test with locust: parse command line options and run commands.
|
"""Performance test with locust: parse command line options and run commands."""
|
||||||
"""
|
|
||||||
print("HttpRunner version: {}".format(__version__))
|
print("HttpRunner version: {}".format(__version__))
|
||||||
sys.argv[0] = 'locust'
|
sys.argv[0] = "locust"
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
sys.argv.extend(["-h"])
|
sys.argv.extend(["-h"])
|
||||||
|
|
||||||
@@ -147,29 +149,36 @@ def main():
|
|||||||
sys.argv[testcase_index] = parse_locustfile(testcase_file_path)
|
sys.argv[testcase_index] = parse_locustfile(testcase_file_path)
|
||||||
|
|
||||||
if "--processes" in sys.argv:
|
if "--processes" in sys.argv:
|
||||||
""" locusts -f locustfile.py --processes 4
|
"""locusts -f locustfile.py --processes 4"""
|
||||||
"""
|
|
||||||
if "--no-web" in sys.argv:
|
if "--no-web" in sys.argv:
|
||||||
logger.log_error("conflict parameter args: --processes & --no-web. \nexit.")
|
logger.log_error("conflict parameter args: --processes & --no-web. \nexit.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
processes_index = sys.argv.index('--processes')
|
processes_index = sys.argv.index("--processes")
|
||||||
processes_count_index = processes_index + 1
|
processes_count_index = processes_index + 1
|
||||||
if processes_count_index >= len(sys.argv):
|
if processes_count_index >= len(sys.argv):
|
||||||
""" do not specify processes count explicitly
|
"""do not specify processes count explicitly
|
||||||
locusts -f locustfile.py --processes
|
locusts -f locustfile.py --processes
|
||||||
"""
|
"""
|
||||||
processes_count = multiprocessing.cpu_count()
|
processes_count = multiprocessing.cpu_count()
|
||||||
logger.log_warning("processes count not specified, use {} by default.".format(processes_count))
|
logger.log_warning(
|
||||||
|
"processes count not specified, use {} by default.".format(
|
||||||
|
processes_count
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
""" locusts -f locustfile.py --processes 4 """
|
"""locusts -f locustfile.py --processes 4"""
|
||||||
processes_count = int(sys.argv[processes_count_index])
|
processes_count = int(sys.argv[processes_count_index])
|
||||||
sys.argv.pop(processes_count_index)
|
sys.argv.pop(processes_count_index)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
""" locusts -f locustfile.py --processes -P 8888 """
|
"""locusts -f locustfile.py --processes -P 8888"""
|
||||||
processes_count = multiprocessing.cpu_count()
|
processes_count = multiprocessing.cpu_count()
|
||||||
logger.log_warning("processes count not specified, use {} by default.".format(processes_count))
|
logger.log_warning(
|
||||||
|
"processes count not specified, use {} by default.".format(
|
||||||
|
processes_count
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
sys.argv.pop(processes_index)
|
sys.argv.pop(processes_index)
|
||||||
run_locusts_with_processes(sys.argv, processes_count)
|
run_locusts_with_processes(sys.argv, processes_count)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from httprunner.ext.locusts.utils import prepare_locust_tests
|
|||||||
from httprunner.runner import Runner
|
from httprunner.runner import Runner
|
||||||
|
|
||||||
logging.getLogger().setLevel(logging.CRITICAL)
|
logging.getLogger().setLevel(logging.CRITICAL)
|
||||||
logging.getLogger('locust.main').setLevel(logging.INFO)
|
logging.getLogger("locust.main").setLevel(logging.INFO)
|
||||||
logging.getLogger('locust.runners').setLevel(logging.INFO)
|
logging.getLogger("locust.runners").setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
class WebPageTasks(TaskSet):
|
class WebPageTasks(TaskSet):
|
||||||
@@ -28,7 +28,7 @@ class WebPageTasks(TaskSet):
|
|||||||
request_type=self.test_runner.exception_request_type,
|
request_type=self.test_runner.exception_request_type,
|
||||||
name=self.test_runner.exception_name,
|
name=self.test_runner.exception_name,
|
||||||
response_time=0,
|
response_time=0,
|
||||||
exception=ex
|
exception=ex,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from httprunner import loader, parser
|
|||||||
|
|
||||||
|
|
||||||
def prepare_locust_tests(path):
|
def prepare_locust_tests(path):
|
||||||
""" prepare locust testcases
|
"""prepare locust testcases
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str): testcase file path.
|
path (str): testcase file path.
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ from httprunner.exceptions import ParamsError
|
|||||||
|
|
||||||
|
|
||||||
def prepare_upload_test(test_dict):
|
def prepare_upload_test(test_dict):
|
||||||
""" preprocess for upload test
|
"""preprocess for upload test
|
||||||
replace `upload` info with MultipartEncoder
|
replace `upload` info with MultipartEncoder
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -96,14 +96,15 @@ def prepare_upload_test(test_dict):
|
|||||||
test_dict["variables"]["m_encoder"] = "${multipart_encoder(" + params_str + ")}"
|
test_dict["variables"]["m_encoder"] = "${multipart_encoder(" + params_str + ")}"
|
||||||
|
|
||||||
test_dict["request"].setdefault("headers", {})
|
test_dict["request"].setdefault("headers", {})
|
||||||
test_dict["request"]["headers"]["Content-Type"] = "${multipart_content_type($m_encoder)}"
|
test_dict["request"]["headers"][
|
||||||
|
"Content-Type"
|
||||||
|
] = "${multipart_content_type($m_encoder)}"
|
||||||
|
|
||||||
test_dict["request"]["data"] = "$m_encoder"
|
test_dict["request"]["data"] = "$m_encoder"
|
||||||
|
|
||||||
|
|
||||||
def multipart_encoder(**kwargs):
|
def multipart_encoder(**kwargs):
|
||||||
""" initialize MultipartEncoder with uploading fields.
|
"""initialize MultipartEncoder with uploading fields."""
|
||||||
"""
|
|
||||||
|
|
||||||
def get_filetype(file_path):
|
def get_filetype(file_path):
|
||||||
file_type = filetype.guess(file_path)
|
file_type = filetype.guess(file_path)
|
||||||
@@ -122,6 +123,7 @@ def multipart_encoder(**kwargs):
|
|||||||
else:
|
else:
|
||||||
# value is not absolute file path, check if it is relative file path
|
# value is not absolute file path, check if it is relative file path
|
||||||
from httprunner.loader import get_pwd
|
from httprunner.loader import get_pwd
|
||||||
|
|
||||||
_file_path = os.path.join(get_pwd(), value)
|
_file_path = os.path.join(get_pwd(), value)
|
||||||
is_exists_file = os.path.isfile(_file_path)
|
is_exists_file = os.path.isfile(_file_path)
|
||||||
|
|
||||||
@@ -130,7 +132,7 @@ def multipart_encoder(**kwargs):
|
|||||||
filename = os.path.basename(_file_path)
|
filename = os.path.basename(_file_path)
|
||||||
mime_type = get_filetype(_file_path)
|
mime_type = get_filetype(_file_path)
|
||||||
# TODO: fix ResourceWarning for unclosed file
|
# TODO: fix ResourceWarning for unclosed file
|
||||||
file_handler = open(_file_path, 'rb')
|
file_handler = open(_file_path, "rb")
|
||||||
fields_dict[key] = (filename, file_handler, mime_type)
|
fields_dict[key] = (filename, file_handler, mime_type)
|
||||||
else:
|
else:
|
||||||
fields_dict[key] = value
|
fields_dict[key] = value
|
||||||
@@ -139,6 +141,5 @@ def multipart_encoder(**kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def multipart_content_type(m_encoder):
|
def multipart_content_type(m_encoder):
|
||||||
""" prepare Content-Type for request headers
|
"""prepare Content-Type for request headers"""
|
||||||
"""
|
|
||||||
return m_encoder.content_type
|
return m_encoder.content_type
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ HttpRunner loader
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from httprunner.loader.check import is_test_path, is_test_content, JsonSchemaChecker
|
from httprunner.loader.check import is_test_path, is_test_content, JsonSchemaChecker
|
||||||
from httprunner.loader.locate import get_project_working_directory as get_pwd, \
|
from httprunner.loader.locate import (
|
||||||
init_project_working_directory as init_pwd
|
get_project_working_directory as get_pwd,
|
||||||
|
init_project_working_directory as init_pwd,
|
||||||
|
)
|
||||||
from httprunner.loader.load import load_csv_file, load_builtin_functions
|
from httprunner.loader.load import load_csv_file, load_builtin_functions
|
||||||
from httprunner.loader.buildup import load_cases, load_project_data
|
from httprunner.loader.buildup import load_cases, load_project_data
|
||||||
|
|
||||||
@@ -23,5 +25,5 @@ __all__ = [
|
|||||||
"load_csv_file",
|
"load_csv_file",
|
||||||
"load_builtin_functions",
|
"load_builtin_functions",
|
||||||
"load_project_data",
|
"load_project_data",
|
||||||
"load_cases"
|
"load_cases",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,18 +4,22 @@ import sys
|
|||||||
|
|
||||||
from httprunner import exceptions, logger, utils
|
from httprunner import exceptions, logger, utils
|
||||||
from httprunner.loader.check import JsonSchemaChecker
|
from httprunner.loader.check import JsonSchemaChecker
|
||||||
from httprunner.loader.load import load_module_functions, load_file, load_dot_env_file, \
|
from httprunner.loader.load import (
|
||||||
load_folder_files
|
load_module_functions,
|
||||||
from httprunner.loader.locate import init_project_working_directory, get_project_working_directory
|
load_file,
|
||||||
|
load_dot_env_file,
|
||||||
|
load_folder_files,
|
||||||
|
)
|
||||||
|
from httprunner.loader.locate import (
|
||||||
|
init_project_working_directory,
|
||||||
|
get_project_working_directory,
|
||||||
|
)
|
||||||
|
|
||||||
tests_def_mapping = {
|
tests_def_mapping = {"api": {}, "testcases": {}}
|
||||||
"api": {},
|
|
||||||
"testcases": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def load_debugtalk_functions():
|
def load_debugtalk_functions():
|
||||||
""" load project debugtalk.py module functions
|
"""load project debugtalk.py module functions
|
||||||
debugtalk.py should be located in project working directory.
|
debugtalk.py should be located in project working directory.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -27,8 +31,8 @@ def load_debugtalk_functions():
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
# load debugtalk.py module
|
# load debugtalk.py module
|
||||||
if sys.modules.get('debugtalk'):
|
if sys.modules.get("debugtalk"):
|
||||||
imported_module = importlib.reload(sys.modules['debugtalk'])
|
imported_module = importlib.reload(sys.modules["debugtalk"])
|
||||||
else:
|
else:
|
||||||
imported_module = importlib.import_module("debugtalk")
|
imported_module = importlib.import_module("debugtalk")
|
||||||
|
|
||||||
@@ -36,7 +40,7 @@ def load_debugtalk_functions():
|
|||||||
|
|
||||||
|
|
||||||
def __extend_with_api_ref(raw_testinfo):
|
def __extend_with_api_ref(raw_testinfo):
|
||||||
""" extend with api reference
|
"""extend with api reference
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
exceptions.ApiNotFound: api not found
|
exceptions.ApiNotFound: api not found
|
||||||
@@ -68,17 +72,13 @@ def __extend_with_api_ref(raw_testinfo):
|
|||||||
|
|
||||||
|
|
||||||
def __extend_with_testcase_ref(raw_testinfo):
|
def __extend_with_testcase_ref(raw_testinfo):
|
||||||
""" extend with testcase reference
|
"""extend with testcase reference"""
|
||||||
"""
|
|
||||||
testcase_path = raw_testinfo["testcase"]
|
testcase_path = raw_testinfo["testcase"]
|
||||||
|
|
||||||
if testcase_path not in tests_def_mapping["testcases"]:
|
if testcase_path not in tests_def_mapping["testcases"]:
|
||||||
# make compatible with Windows/Linux
|
# make compatible with Windows/Linux
|
||||||
pwd = get_project_working_directory()
|
pwd = get_project_working_directory()
|
||||||
testcase_path = os.path.join(
|
testcase_path = os.path.join(pwd, *testcase_path.split("/"))
|
||||||
pwd,
|
|
||||||
*testcase_path.split("/")
|
|
||||||
)
|
|
||||||
loaded_testcase = load_file(testcase_path)
|
loaded_testcase = load_file(testcase_path)
|
||||||
|
|
||||||
if isinstance(loaded_testcase, list):
|
if isinstance(loaded_testcase, list):
|
||||||
@@ -89,7 +89,8 @@ def __extend_with_testcase_ref(raw_testinfo):
|
|||||||
testcase_dict = load_testcase_v2(loaded_testcase)
|
testcase_dict = load_testcase_v2(loaded_testcase)
|
||||||
else:
|
else:
|
||||||
raise exceptions.FileFormatError(
|
raise exceptions.FileFormatError(
|
||||||
"Invalid format testcase: {}".format(testcase_path))
|
"Invalid format testcase: {}".format(testcase_path)
|
||||||
|
)
|
||||||
|
|
||||||
tests_def_mapping["testcases"][testcase_path] = testcase_dict
|
tests_def_mapping["testcases"][testcase_path] = testcase_dict
|
||||||
else:
|
else:
|
||||||
@@ -99,7 +100,7 @@ def __extend_with_testcase_ref(raw_testinfo):
|
|||||||
|
|
||||||
|
|
||||||
def load_teststep(raw_testinfo):
|
def load_teststep(raw_testinfo):
|
||||||
""" load testcase step content.
|
"""load testcase step content.
|
||||||
teststep maybe defined directly, or reference api/testcase.
|
teststep maybe defined directly, or reference api/testcase.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -151,7 +152,7 @@ def load_teststep(raw_testinfo):
|
|||||||
|
|
||||||
|
|
||||||
def load_testcase(raw_testcase):
|
def load_testcase(raw_testcase):
|
||||||
""" load testcase with api/testcase references.
|
"""load testcase with api/testcase references.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
raw_testcase (list): raw testcase content loaded from JSON/YAML file:
|
raw_testcase (list): raw testcase content loaded from JSON/YAML file:
|
||||||
@@ -192,17 +193,16 @@ def load_testcase(raw_testcase):
|
|||||||
tests.append(load_teststep(test_block))
|
tests.append(load_teststep(test_block))
|
||||||
else:
|
else:
|
||||||
logger.log_warning(
|
logger.log_warning(
|
||||||
"unexpected block key: {}. block key should only be 'config' or 'test'.".format(key)
|
"unexpected block key: {}. block key should only be 'config' or 'test'.".format(
|
||||||
|
key
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {"config": config, "teststeps": tests}
|
||||||
"config": config,
|
|
||||||
"teststeps": tests
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def load_testcase_v2(raw_testcase):
|
def load_testcase_v2(raw_testcase):
|
||||||
""" load testcase in format version 2.
|
"""load testcase in format version 2.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
raw_testcase (dict): raw testcase content loaded from JSON/YAML file:
|
raw_testcase (dict): raw testcase content loaded from JSON/YAML file:
|
||||||
@@ -233,15 +233,12 @@ def load_testcase_v2(raw_testcase):
|
|||||||
"""
|
"""
|
||||||
JsonSchemaChecker.validate_testcase_v2_format(raw_testcase)
|
JsonSchemaChecker.validate_testcase_v2_format(raw_testcase)
|
||||||
raw_teststeps = raw_testcase.pop("teststeps")
|
raw_teststeps = raw_testcase.pop("teststeps")
|
||||||
raw_testcase["teststeps"] = [
|
raw_testcase["teststeps"] = [load_teststep(teststep) for teststep in raw_teststeps]
|
||||||
load_teststep(teststep)
|
|
||||||
for teststep in raw_teststeps
|
|
||||||
]
|
|
||||||
return raw_testcase
|
return raw_testcase
|
||||||
|
|
||||||
|
|
||||||
def load_testsuite(raw_testsuite):
|
def load_testsuite(raw_testsuite):
|
||||||
""" load testsuite with testcase references.
|
"""load testsuite with testcase references.
|
||||||
support two different formats.
|
support two different formats.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -315,7 +312,7 @@ def load_testsuite(raw_testsuite):
|
|||||||
|
|
||||||
|
|
||||||
def load_test_file(path):
|
def load_test_file(path):
|
||||||
""" load test file, file maybe testcase/testsuite/api
|
"""load test file, file maybe testcase/testsuite/api
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str): test file path
|
path (str): test file path
|
||||||
@@ -390,7 +387,7 @@ def load_test_file(path):
|
|||||||
|
|
||||||
|
|
||||||
def load_project_data(test_path, dot_env_path=None):
|
def load_project_data(test_path, dot_env_path=None):
|
||||||
""" load api, testcases, .env, debugtalk.py functions.
|
"""load api, testcases, .env, debugtalk.py functions.
|
||||||
api/testcases folder is relative to project_working_directory
|
api/testcases folder is relative to project_working_directory
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -402,7 +399,9 @@ def load_project_data(test_path, dot_env_path=None):
|
|||||||
environments and debugtalk.py functions.
|
environments and debugtalk.py functions.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
debugtalk_path, project_working_directory = init_project_working_directory(test_path)
|
debugtalk_path, project_working_directory = init_project_working_directory(
|
||||||
|
test_path
|
||||||
|
)
|
||||||
|
|
||||||
project_mapping = {}
|
project_mapping = {}
|
||||||
|
|
||||||
@@ -428,7 +427,7 @@ def load_project_data(test_path, dot_env_path=None):
|
|||||||
|
|
||||||
|
|
||||||
def load_cases(path, dot_env_path=None):
|
def load_cases(path, dot_env_path=None):
|
||||||
""" load testcases from file path, extend and merge with api/testcase definitions.
|
"""load testcases from file path, extend and merge with api/testcase definitions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str): testcase/testsuite file/foler path.
|
path (str): testcase/testsuite file/foler path.
|
||||||
@@ -481,9 +480,7 @@ def load_cases(path, dot_env_path=None):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tests_mapping = {
|
tests_mapping = {"project_mapping": load_project_data(path, dot_env_path)}
|
||||||
"project_mapping": load_project_data(path, dot_env_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
def __load_file_content(path):
|
def __load_file_content(path):
|
||||||
loaded_content = None
|
loaded_content = None
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ testcase_schema_v2_path = os.path.join(schemas_root_dir, "testcase.schema.v2.jso
|
|||||||
testsuite_schema_v1_path = os.path.join(schemas_root_dir, "testsuite.schema.v1.json")
|
testsuite_schema_v1_path = os.path.join(schemas_root_dir, "testsuite.schema.v1.json")
|
||||||
testsuite_schema_v2_path = os.path.join(schemas_root_dir, "testsuite.schema.v2.json")
|
testsuite_schema_v2_path = os.path.join(schemas_root_dir, "testsuite.schema.v2.json")
|
||||||
|
|
||||||
with io.open(api_schema_path, encoding='utf-8') as f:
|
with io.open(api_schema_path, encoding="utf-8") as f:
|
||||||
api_schema = json.load(f)
|
api_schema = json.load(f)
|
||||||
|
|
||||||
with io.open(common_schema_path, encoding='utf-8') as f:
|
with io.open(common_schema_path, encoding="utf-8") as f:
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
absolute_base_path = 'file:///' + os.path.abspath(schemas_root_dir).replace("\\", "/") + '/'
|
absolute_base_path = (
|
||||||
|
"file:///" + os.path.abspath(schemas_root_dir).replace("\\", "/") + "/"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# Linux, Darwin
|
# Linux, Darwin
|
||||||
absolute_base_path = "file://" + os.path.abspath(schemas_root_dir) + "/"
|
absolute_base_path = "file://" + os.path.abspath(schemas_root_dir) + "/"
|
||||||
@@ -28,25 +30,23 @@ with io.open(common_schema_path, encoding='utf-8') as f:
|
|||||||
common_schema = json.load(f)
|
common_schema = json.load(f)
|
||||||
resolver = jsonschema.RefResolver(absolute_base_path, common_schema)
|
resolver = jsonschema.RefResolver(absolute_base_path, common_schema)
|
||||||
|
|
||||||
with io.open(testcase_schema_v1_path, encoding='utf-8') as f:
|
with io.open(testcase_schema_v1_path, encoding="utf-8") as f:
|
||||||
testcase_schema_v1 = json.load(f)
|
testcase_schema_v1 = json.load(f)
|
||||||
|
|
||||||
with io.open(testcase_schema_v2_path, encoding='utf-8') as f:
|
with io.open(testcase_schema_v2_path, encoding="utf-8") as f:
|
||||||
testcase_schema_v2 = json.load(f)
|
testcase_schema_v2 = json.load(f)
|
||||||
|
|
||||||
with io.open(testsuite_schema_v1_path, encoding='utf-8') as f:
|
with io.open(testsuite_schema_v1_path, encoding="utf-8") as f:
|
||||||
testsuite_schema_v1 = json.load(f)
|
testsuite_schema_v1 = json.load(f)
|
||||||
|
|
||||||
with io.open(testsuite_schema_v2_path, encoding='utf-8') as f:
|
with io.open(testsuite_schema_v2_path, encoding="utf-8") as f:
|
||||||
testsuite_schema_v2 = json.load(f)
|
testsuite_schema_v2 = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
class JsonSchemaChecker(object):
|
class JsonSchemaChecker(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_format(content, scheme):
|
def validate_format(content, scheme):
|
||||||
""" check api/testcase/testsuite format if valid
|
"""check api/testcase/testsuite format if valid"""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
jsonschema.validate(content, scheme, resolver=resolver)
|
jsonschema.validate(content, scheme, resolver=resolver)
|
||||||
except jsonschema.exceptions.ValidationError as ex:
|
except jsonschema.exceptions.ValidationError as ex:
|
||||||
@@ -57,37 +57,32 @@ class JsonSchemaChecker(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_api_format(content):
|
def validate_api_format(content):
|
||||||
""" check api format if valid
|
"""check api format if valid"""
|
||||||
"""
|
|
||||||
return JsonSchemaChecker.validate_format(content, api_schema)
|
return JsonSchemaChecker.validate_format(content, api_schema)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_testcase_v1_format(content):
|
def validate_testcase_v1_format(content):
|
||||||
""" check testcase format v1 if valid
|
"""check testcase format v1 if valid"""
|
||||||
"""
|
|
||||||
return JsonSchemaChecker.validate_format(content, testcase_schema_v1)
|
return JsonSchemaChecker.validate_format(content, testcase_schema_v1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_testcase_v2_format(content):
|
def validate_testcase_v2_format(content):
|
||||||
""" check testcase format v2 if valid
|
"""check testcase format v2 if valid"""
|
||||||
"""
|
|
||||||
return JsonSchemaChecker.validate_format(content, testcase_schema_v2)
|
return JsonSchemaChecker.validate_format(content, testcase_schema_v2)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_testsuite_v1_format(content):
|
def validate_testsuite_v1_format(content):
|
||||||
""" check testsuite format v1 if valid
|
"""check testsuite format v1 if valid"""
|
||||||
"""
|
|
||||||
return JsonSchemaChecker.validate_format(content, testsuite_schema_v1)
|
return JsonSchemaChecker.validate_format(content, testsuite_schema_v1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_testsuite_v2_format(content):
|
def validate_testsuite_v2_format(content):
|
||||||
""" check testsuite format v2 if valid
|
"""check testsuite format v2 if valid"""
|
||||||
"""
|
|
||||||
return JsonSchemaChecker.validate_format(content, testsuite_schema_v2)
|
return JsonSchemaChecker.validate_format(content, testsuite_schema_v2)
|
||||||
|
|
||||||
|
|
||||||
def is_test_path(path):
|
def is_test_path(path):
|
||||||
""" check if path is valid json/yaml file path or a existed directory.
|
"""check if path is valid json/yaml file path or a existed directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str/list/tuple): file path/directory or file path list.
|
path (str/list/tuple): file path/directory or file path list.
|
||||||
@@ -115,7 +110,7 @@ def is_test_path(path):
|
|||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
# path is a file
|
# path is a file
|
||||||
file_suffix = os.path.splitext(path)[1].lower()
|
file_suffix = os.path.splitext(path)[1].lower()
|
||||||
if file_suffix not in ['.json', '.yaml', '.yml']:
|
if file_suffix not in [".json", ".yaml", ".yml"]:
|
||||||
# path is not json/yaml file
|
# path is not json/yaml file
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@@ -129,7 +124,7 @@ def is_test_path(path):
|
|||||||
|
|
||||||
|
|
||||||
def is_test_content(data_structure):
|
def is_test_content(data_structure):
|
||||||
""" check if data_structure is apis/testcases/testsuites.
|
"""check if data_structure is apis/testcases/testsuites.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data_structure (dict): should include keys, apis or testcases or testsuites
|
data_structure (dict): should include keys, apis or testcases or testsuites
|
||||||
|
|||||||
@@ -13,15 +13,14 @@ from httprunner.loader.locate import get_project_working_directory
|
|||||||
try:
|
try:
|
||||||
# PyYAML version >= 5.1
|
# PyYAML version >= 5.1
|
||||||
# ref: https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation
|
# ref: https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation
|
||||||
yaml.warnings({'YAMLLoadWarning': False})
|
yaml.warnings({"YAMLLoadWarning": False})
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _load_yaml_file(yaml_file):
|
def _load_yaml_file(yaml_file):
|
||||||
""" load yaml file and check file content format
|
"""load yaml file and check file content format"""
|
||||||
"""
|
with io.open(yaml_file, "r", encoding="utf-8") as stream:
|
||||||
with io.open(yaml_file, 'r', encoding='utf-8') as stream:
|
|
||||||
try:
|
try:
|
||||||
yaml_content = yaml.load(stream)
|
yaml_content = yaml.load(stream)
|
||||||
except yaml.YAMLError as ex:
|
except yaml.YAMLError as ex:
|
||||||
@@ -32,13 +31,12 @@ def _load_yaml_file(yaml_file):
|
|||||||
|
|
||||||
|
|
||||||
def _load_json_file(json_file):
|
def _load_json_file(json_file):
|
||||||
""" load json file and check file content format
|
"""load json file and check file content format"""
|
||||||
"""
|
with io.open(json_file, encoding="utf-8") as data_file:
|
||||||
with io.open(json_file, encoding='utf-8') as data_file:
|
|
||||||
try:
|
try:
|
||||||
json_content = json.load(data_file)
|
json_content = json.load(data_file)
|
||||||
except exceptions.JSONDecodeError:
|
except exceptions.JSONDecodeError:
|
||||||
err_msg = u"JSONDecodeError: JSON file format error: {}".format(json_file)
|
err_msg = "JSONDecodeError: JSON file format error: {}".format(json_file)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.FileFormatError(err_msg)
|
raise exceptions.FileFormatError(err_msg)
|
||||||
|
|
||||||
@@ -46,7 +44,7 @@ def _load_json_file(json_file):
|
|||||||
|
|
||||||
|
|
||||||
def load_csv_file(csv_file):
|
def load_csv_file(csv_file):
|
||||||
""" load csv file and check file content format
|
"""load csv file and check file content format
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
csv_file (str): csv file path, csv file content is like below:
|
csv_file (str): csv file path, csv file content is like below:
|
||||||
@@ -80,7 +78,7 @@ def load_csv_file(csv_file):
|
|||||||
|
|
||||||
csv_content_list = []
|
csv_content_list = []
|
||||||
|
|
||||||
with io.open(csv_file, encoding='utf-8') as csvfile:
|
with io.open(csv_file, encoding="utf-8") as csvfile:
|
||||||
reader = csv.DictReader(csvfile)
|
reader = csv.DictReader(csvfile)
|
||||||
for row in reader:
|
for row in reader:
|
||||||
csv_content_list.append(row)
|
csv_content_list.append(row)
|
||||||
@@ -93,21 +91,21 @@ def load_file(file_path):
|
|||||||
raise exceptions.FileNotFound("{} does not exist.".format(file_path))
|
raise exceptions.FileNotFound("{} does not exist.".format(file_path))
|
||||||
|
|
||||||
file_suffix = os.path.splitext(file_path)[1].lower()
|
file_suffix = os.path.splitext(file_path)[1].lower()
|
||||||
if file_suffix == '.json':
|
if file_suffix == ".json":
|
||||||
return _load_json_file(file_path)
|
return _load_json_file(file_path)
|
||||||
elif file_suffix in ['.yaml', '.yml']:
|
elif file_suffix in [".yaml", ".yml"]:
|
||||||
return _load_yaml_file(file_path)
|
return _load_yaml_file(file_path)
|
||||||
elif file_suffix == ".csv":
|
elif file_suffix == ".csv":
|
||||||
return load_csv_file(file_path)
|
return load_csv_file(file_path)
|
||||||
else:
|
else:
|
||||||
# '' or other suffix
|
# '' or other suffix
|
||||||
err_msg = u"Unsupported file format: {}".format(file_path)
|
err_msg = "Unsupported file format: {}".format(file_path)
|
||||||
logger.log_warning(err_msg)
|
logger.log_warning(err_msg)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def load_folder_files(folder_path, recursive=True):
|
def load_folder_files(folder_path, recursive=True):
|
||||||
""" load folder path, return all files endswith yml/yaml/json in list.
|
"""load folder path, return all files endswith yml/yaml/json in list.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
folder_path (str): specified folder path to load
|
folder_path (str): specified folder path to load
|
||||||
@@ -132,7 +130,7 @@ def load_folder_files(folder_path, recursive=True):
|
|||||||
filenames_list = []
|
filenames_list = []
|
||||||
|
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if not filename.endswith(('.yml', '.yaml', '.json')):
|
if not filename.endswith((".yml", ".yaml", ".json")):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
filenames_list.append(filename)
|
filenames_list.append(filename)
|
||||||
@@ -148,7 +146,7 @@ def load_folder_files(folder_path, recursive=True):
|
|||||||
|
|
||||||
|
|
||||||
def load_dot_env_file(dot_env_path):
|
def load_dot_env_file(dot_env_path):
|
||||||
""" load .env file.
|
"""load .env file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dot_env_path (str): .env file path
|
dot_env_path (str): .env file path
|
||||||
@@ -172,7 +170,7 @@ def load_dot_env_file(dot_env_path):
|
|||||||
logger.log_info("Loading environment variables from {}".format(dot_env_path))
|
logger.log_info("Loading environment variables from {}".format(dot_env_path))
|
||||||
env_variables_mapping = {}
|
env_variables_mapping = {}
|
||||||
|
|
||||||
with io.open(dot_env_path, 'r', encoding='utf-8') as fp:
|
with io.open(dot_env_path, "r", encoding="utf-8") as fp:
|
||||||
for line in fp:
|
for line in fp:
|
||||||
# maxsplit=1
|
# maxsplit=1
|
||||||
if "=" in line:
|
if "=" in line:
|
||||||
@@ -189,7 +187,7 @@ def load_dot_env_file(dot_env_path):
|
|||||||
|
|
||||||
|
|
||||||
def load_module_functions(module):
|
def load_module_functions(module):
|
||||||
""" load python module functions.
|
"""load python module functions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
module: python module
|
module: python module
|
||||||
@@ -213,7 +211,5 @@ def load_module_functions(module):
|
|||||||
|
|
||||||
|
|
||||||
def load_builtin_functions():
|
def load_builtin_functions():
|
||||||
""" load builtin module functions
|
"""load builtin module functions"""
|
||||||
"""
|
|
||||||
return load_module_functions(builtin)
|
return load_module_functions(builtin)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ project_working_directory = None
|
|||||||
|
|
||||||
|
|
||||||
def locate_file(start_path, file_name):
|
def locate_file(start_path, file_name):
|
||||||
""" locate filename and return absolute file path.
|
"""locate filename and return absolute file path.
|
||||||
searching will be recursive upward until current working directory or system root dir.
|
searching will be recursive upward until current working directory or system root dir.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -34,21 +34,25 @@ def locate_file(start_path, file_name):
|
|||||||
|
|
||||||
# current working directory
|
# current working directory
|
||||||
if os.path.abspath(start_dir_path) == os.getcwd():
|
if os.path.abspath(start_dir_path) == os.getcwd():
|
||||||
raise exceptions.FileNotFound("{} not found in {}".format(file_name, start_path))
|
raise exceptions.FileNotFound(
|
||||||
|
"{} not found in {}".format(file_name, start_path)
|
||||||
|
)
|
||||||
|
|
||||||
# system root dir
|
# system root dir
|
||||||
# Windows, e.g. 'E:\\'
|
# Windows, e.g. 'E:\\'
|
||||||
# Linux/Darwin, '/'
|
# Linux/Darwin, '/'
|
||||||
parent_dir = os.path.dirname(start_dir_path)
|
parent_dir = os.path.dirname(start_dir_path)
|
||||||
if parent_dir == start_dir_path:
|
if parent_dir == start_dir_path:
|
||||||
raise exceptions.FileNotFound("{} not found in {}".format(file_name, start_path))
|
raise exceptions.FileNotFound(
|
||||||
|
"{} not found in {}".format(file_name, start_path)
|
||||||
|
)
|
||||||
|
|
||||||
# locate recursive upward
|
# locate recursive upward
|
||||||
return locate_file(parent_dir, file_name)
|
return locate_file(parent_dir, file_name)
|
||||||
|
|
||||||
|
|
||||||
def locate_debugtalk_py(start_path):
|
def locate_debugtalk_py(start_path):
|
||||||
""" locate debugtalk.py file
|
"""locate debugtalk.py file
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
start_path (str): start locating path,
|
start_path (str): start locating path,
|
||||||
@@ -68,7 +72,7 @@ def locate_debugtalk_py(start_path):
|
|||||||
|
|
||||||
|
|
||||||
def init_project_working_directory(test_path):
|
def init_project_working_directory(test_path):
|
||||||
""" this should be called at startup
|
"""this should be called at startup
|
||||||
|
|
||||||
run test file:
|
run test file:
|
||||||
run_path -> load_cases -> load_project_data -> init_project_working_directory
|
run_path -> load_cases -> load_project_data -> init_project_working_directory
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ LOG_LEVEL = "INFO"
|
|||||||
LOG_FILE_PATH = ""
|
LOG_FILE_PATH = ""
|
||||||
|
|
||||||
log_colors_config = {
|
log_colors_config = {
|
||||||
'DEBUG': 'cyan',
|
"DEBUG": "cyan",
|
||||||
'INFO': 'green',
|
"INFO": "green",
|
||||||
'WARNING': 'yellow',
|
"WARNING": "yellow",
|
||||||
'ERROR': 'red',
|
"ERROR": "red",
|
||||||
'CRITICAL': 'red',
|
"CRITICAL": "red",
|
||||||
}
|
}
|
||||||
loggers = {}
|
loggers = {}
|
||||||
|
|
||||||
@@ -58,10 +58,10 @@ def get_logger(name=None):
|
|||||||
handler = logging.StreamHandler(sys.stdout)
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
|
|
||||||
formatter = ColoredFormatter(
|
formatter = ColoredFormatter(
|
||||||
u"%(log_color)s%(bg_white)s%(levelname)-8s%(reset)s %(message)s",
|
"%(log_color)s%(bg_white)s%(levelname)-8s%(reset)s %(message)s",
|
||||||
datefmt=None,
|
datefmt=None,
|
||||||
reset=True,
|
reset=True,
|
||||||
log_colors=log_colors_config
|
log_colors=log_colors_config,
|
||||||
)
|
)
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
_logger.addHandler(handler)
|
_logger.addHandler(handler)
|
||||||
@@ -81,8 +81,8 @@ def color_print(msg, color="WHITE"):
|
|||||||
|
|
||||||
|
|
||||||
def log_with_color(level):
|
def log_with_color(level):
|
||||||
""" log with color by different level
|
"""log with color by different level"""
|
||||||
"""
|
|
||||||
def wrapper(text):
|
def wrapper(text):
|
||||||
color = log_colors_config[level.upper()]
|
color = log_colors_config[level.upper()]
|
||||||
_logger = get_logger()
|
_logger = get_logger()
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ def get_parse_failed_testfiles():
|
|||||||
|
|
||||||
|
|
||||||
def parse_string_value(str_value):
|
def parse_string_value(str_value):
|
||||||
""" parse string to number if possible
|
"""parse string to number if possible
|
||||||
e.g. "123" => 123
|
e.g. "123" => 123
|
||||||
"12.2" => 12.3
|
"12.2" => 12.3
|
||||||
"abc" => "abc"
|
"abc" => "abc"
|
||||||
@@ -43,8 +43,7 @@ def parse_string_value(str_value):
|
|||||||
|
|
||||||
|
|
||||||
def is_var_or_func_exist(content):
|
def is_var_or_func_exist(content):
|
||||||
""" check if variable or function exist
|
"""check if variable or function exist"""
|
||||||
"""
|
|
||||||
if not isinstance(content, basestring):
|
if not isinstance(content, basestring):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -71,7 +70,7 @@ def is_var_or_func_exist(content):
|
|||||||
|
|
||||||
|
|
||||||
def regex_findall_variables(content):
|
def regex_findall_variables(content):
|
||||||
""" extract all variable names from content, which is in format $variable
|
"""extract all variable names from content, which is in format $variable
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
content (str): string content
|
content (str): string content
|
||||||
@@ -96,16 +95,14 @@ def regex_findall_variables(content):
|
|||||||
try:
|
try:
|
||||||
vars_list = []
|
vars_list = []
|
||||||
for var_tuple in variable_regex_compile.findall(content):
|
for var_tuple in variable_regex_compile.findall(content):
|
||||||
vars_list.append(
|
vars_list.append(var_tuple[0] or var_tuple[1])
|
||||||
var_tuple[0] or var_tuple[1]
|
|
||||||
)
|
|
||||||
return vars_list
|
return vars_list
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def regex_findall_functions(content):
|
def regex_findall_functions(content):
|
||||||
""" extract all functions from string content, which are in format ${fun()}
|
"""extract all functions from string content, which are in format ${fun()}
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
content (str): string content
|
content (str): string content
|
||||||
@@ -137,7 +134,7 @@ def regex_findall_functions(content):
|
|||||||
|
|
||||||
|
|
||||||
def parse_parameters(parameters, variables_mapping=None, functions_mapping=None):
|
def parse_parameters(parameters, variables_mapping=None, functions_mapping=None):
|
||||||
""" parse parameters and generate cartesian product.
|
"""parse parameters and generate cartesian product.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
parameters (list) parameters: parameter name and value in list
|
parameters (list) parameters: parameter name and value in list
|
||||||
@@ -188,13 +185,9 @@ def parse_parameters(parameters, variables_mapping=None, functions_mapping=None)
|
|||||||
parameter_content_list.append(parameter_content_dict)
|
parameter_content_list.append(parameter_content_dict)
|
||||||
else:
|
else:
|
||||||
# (2) & (3)
|
# (2) & (3)
|
||||||
parsed_variables_mapping = parse_variables_mapping(
|
parsed_variables_mapping = parse_variables_mapping(variables_mapping)
|
||||||
variables_mapping
|
|
||||||
)
|
|
||||||
parsed_parameter_content = eval_lazy_data(
|
parsed_parameter_content = eval_lazy_data(
|
||||||
parameter_content,
|
parameter_content, parsed_variables_mapping, functions_mapping
|
||||||
parsed_variables_mapping,
|
|
||||||
functions_mapping
|
|
||||||
)
|
)
|
||||||
if not isinstance(parsed_parameter_content, list):
|
if not isinstance(parsed_parameter_content, list):
|
||||||
raise exceptions.ParamsError("parameters syntax error!")
|
raise exceptions.ParamsError("parameters syntax error!")
|
||||||
@@ -210,7 +203,9 @@ def parse_parameters(parameters, variables_mapping=None, functions_mapping=None)
|
|||||||
# {"username": "user1", "password": "111111"},
|
# {"username": "user1", "password": "111111"},
|
||||||
# {"username": "user2", "password": "222222"}
|
# {"username": "user2", "password": "222222"}
|
||||||
# ]
|
# ]
|
||||||
parameter_dict = {key: parameter_item[key] for key in parameter_name_list}
|
parameter_dict = {
|
||||||
|
key: parameter_item[key] for key in parameter_name_list
|
||||||
|
}
|
||||||
elif isinstance(parameter_item, (list, tuple)):
|
elif isinstance(parameter_item, (list, tuple)):
|
||||||
# {"username-password": "${get_account()}"}
|
# {"username-password": "${get_account()}"}
|
||||||
# get_account() => [("user1", "111111"), ("user2", "222222")]
|
# get_account() => [("user1", "111111"), ("user2", "222222")]
|
||||||
@@ -218,9 +213,7 @@ def parse_parameters(parameters, variables_mapping=None, functions_mapping=None)
|
|||||||
elif len(parameter_name_list) == 1:
|
elif len(parameter_name_list) == 1:
|
||||||
# {"user_agent": "${get_user_agent()}"}
|
# {"user_agent": "${get_user_agent()}"}
|
||||||
# get_user_agent() => ["iOS/10.1", "iOS/10.2"]
|
# get_user_agent() => ["iOS/10.1", "iOS/10.2"]
|
||||||
parameter_dict = {
|
parameter_dict = {parameter_name_list[0]: parameter_item}
|
||||||
parameter_name_list[0]: parameter_item
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter_content_list.append(parameter_dict)
|
parameter_content_list.append(parameter_dict)
|
||||||
|
|
||||||
@@ -230,8 +223,7 @@ def parse_parameters(parameters, variables_mapping=None, functions_mapping=None)
|
|||||||
|
|
||||||
|
|
||||||
def get_uniform_comparator(comparator):
|
def get_uniform_comparator(comparator):
|
||||||
""" convert comparator alias to uniform name
|
"""convert comparator alias to uniform name"""
|
||||||
"""
|
|
||||||
if comparator in ["eq", "equals", "==", "is"]:
|
if comparator in ["eq", "equals", "==", "is"]:
|
||||||
return "equals"
|
return "equals"
|
||||||
elif comparator in ["lt", "less_than"]:
|
elif comparator in ["lt", "less_than"]:
|
||||||
@@ -248,22 +240,35 @@ def get_uniform_comparator(comparator):
|
|||||||
return "string_equals"
|
return "string_equals"
|
||||||
elif comparator in ["len_eq", "length_equals", "count_eq"]:
|
elif comparator in ["len_eq", "length_equals", "count_eq"]:
|
||||||
return "length_equals"
|
return "length_equals"
|
||||||
elif comparator in ["len_gt", "count_gt", "length_greater_than", "count_greater_than"]:
|
elif comparator in [
|
||||||
|
"len_gt",
|
||||||
|
"count_gt",
|
||||||
|
"length_greater_than",
|
||||||
|
"count_greater_than",
|
||||||
|
]:
|
||||||
return "length_greater_than"
|
return "length_greater_than"
|
||||||
elif comparator in ["len_ge", "count_ge", "length_greater_than_or_equals",
|
elif comparator in [
|
||||||
"count_greater_than_or_equals"]:
|
"len_ge",
|
||||||
|
"count_ge",
|
||||||
|
"length_greater_than_or_equals",
|
||||||
|
"count_greater_than_or_equals",
|
||||||
|
]:
|
||||||
return "length_greater_than_or_equals"
|
return "length_greater_than_or_equals"
|
||||||
elif comparator in ["len_lt", "count_lt", "length_less_than", "count_less_than"]:
|
elif comparator in ["len_lt", "count_lt", "length_less_than", "count_less_than"]:
|
||||||
return "length_less_than"
|
return "length_less_than"
|
||||||
elif comparator in ["len_le", "count_le", "length_less_than_or_equals",
|
elif comparator in [
|
||||||
"count_less_than_or_equals"]:
|
"len_le",
|
||||||
|
"count_le",
|
||||||
|
"length_less_than_or_equals",
|
||||||
|
"count_less_than_or_equals",
|
||||||
|
]:
|
||||||
return "length_less_than_or_equals"
|
return "length_less_than_or_equals"
|
||||||
else:
|
else:
|
||||||
return comparator
|
return comparator
|
||||||
|
|
||||||
|
|
||||||
def uniform_validator(validator):
|
def uniform_validator(validator):
|
||||||
""" unify validator
|
"""unify validator
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
validator (dict): validator maybe in two formats:
|
validator (dict): validator maybe in two formats:
|
||||||
@@ -310,15 +315,11 @@ def uniform_validator(validator):
|
|||||||
# uniform comparator, e.g. lt => less_than, eq => equals
|
# uniform comparator, e.g. lt => less_than, eq => equals
|
||||||
comparator = get_uniform_comparator(comparator)
|
comparator = get_uniform_comparator(comparator)
|
||||||
|
|
||||||
return {
|
return {"check": check_item, "expect": expect_value, "comparator": comparator}
|
||||||
"check": check_item,
|
|
||||||
"expect": expect_value,
|
|
||||||
"comparator": comparator
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _convert_validators_to_mapping(validators):
|
def _convert_validators_to_mapping(validators):
|
||||||
""" convert validators list to mapping.
|
"""convert validators list to mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
validators (list): validators in list
|
validators (list): validators in list
|
||||||
@@ -353,7 +354,7 @@ def _convert_validators_to_mapping(validators):
|
|||||||
|
|
||||||
|
|
||||||
def extend_validators(raw_validators, override_validators):
|
def extend_validators(raw_validators, override_validators):
|
||||||
""" extend raw_validators with override_validators.
|
"""extend raw_validators with override_validators.
|
||||||
override_validators will merge and override raw_validators.
|
override_validators will merge and override raw_validators.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -393,8 +394,9 @@ def extend_validators(raw_validators, override_validators):
|
|||||||
## parse content with variables and functions mapping
|
## parse content with variables and functions mapping
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
def get_mapping_variable(variable_name, variables_mapping):
|
def get_mapping_variable(variable_name, variables_mapping):
|
||||||
""" get variable from variables_mapping.
|
"""get variable from variables_mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
variable_name (str): variable name
|
variable_name (str): variable name
|
||||||
@@ -414,7 +416,7 @@ def get_mapping_variable(variable_name, variables_mapping):
|
|||||||
|
|
||||||
|
|
||||||
def get_mapping_function(function_name, functions_mapping):
|
def get_mapping_function(function_name, functions_mapping):
|
||||||
""" get function from functions_mapping,
|
"""get function from functions_mapping,
|
||||||
if not found, then try to check if builtin function.
|
if not found, then try to check if builtin function.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -440,6 +442,7 @@ def get_mapping_function(function_name, functions_mapping):
|
|||||||
elif function_name in ["multipart_encoder", "multipart_content_type"]:
|
elif function_name in ["multipart_encoder", "multipart_content_type"]:
|
||||||
# extension for upload test
|
# extension for upload test
|
||||||
from httprunner.ext import uploader
|
from httprunner.ext import uploader
|
||||||
|
|
||||||
return getattr(uploader, function_name)
|
return getattr(uploader, function_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -459,7 +462,7 @@ def get_mapping_function(function_name, functions_mapping):
|
|||||||
|
|
||||||
|
|
||||||
def parse_function_params(params):
|
def parse_function_params(params):
|
||||||
""" parse function params to args and kwargs.
|
"""parse function params to args and kwargs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
params (str): function param in string
|
params (str): function param in string
|
||||||
@@ -489,20 +492,17 @@ def parse_function_params(params):
|
|||||||
{'args': [1, 2], 'kwargs': {'a':3, 'b':4}}
|
{'args': [1, 2], 'kwargs': {'a':3, 'b':4}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
function_meta = {
|
function_meta = {"args": [], "kwargs": {}}
|
||||||
"args": [],
|
|
||||||
"kwargs": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
params_str = params.strip()
|
params_str = params.strip()
|
||||||
if params_str == "":
|
if params_str == "":
|
||||||
return function_meta
|
return function_meta
|
||||||
|
|
||||||
args_list = params_str.split(',')
|
args_list = params_str.split(",")
|
||||||
for arg in args_list:
|
for arg in args_list:
|
||||||
arg = arg.strip()
|
arg = arg.strip()
|
||||||
if '=' in arg:
|
if "=" in arg:
|
||||||
key, value = arg.split('=')
|
key, value = arg.split("=")
|
||||||
function_meta["kwargs"][key.strip()] = parse_string_value(value.strip())
|
function_meta["kwargs"][key.strip()] = parse_string_value(value.strip())
|
||||||
else:
|
else:
|
||||||
function_meta["args"].append(parse_string_value(arg))
|
function_meta["args"].append(parse_string_value(arg))
|
||||||
@@ -511,11 +511,10 @@ def parse_function_params(params):
|
|||||||
|
|
||||||
|
|
||||||
class LazyFunction(object):
|
class LazyFunction(object):
|
||||||
""" call function lazily.
|
"""call function lazily."""
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, function_meta, functions_mapping=None, check_variables_set=None):
|
def __init__(self, function_meta, functions_mapping=None, check_variables_set=None):
|
||||||
""" init LazyFunction object with function_meta
|
"""init LazyFunction object with function_meta
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
function_meta (dict): function name, args and kwargs.
|
function_meta (dict): function name, args and kwargs.
|
||||||
@@ -532,25 +531,24 @@ class LazyFunction(object):
|
|||||||
self.__parse(function_meta)
|
self.__parse(function_meta)
|
||||||
|
|
||||||
def __parse(self, function_meta):
|
def __parse(self, function_meta):
|
||||||
""" init func as lazy functon instance
|
"""init func as lazy functon instance
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
function_meta (dict): function meta including name, args and kwargs
|
function_meta (dict): function meta including name, args and kwargs
|
||||||
"""
|
"""
|
||||||
self._func = get_mapping_function(
|
self._func = get_mapping_function(
|
||||||
function_meta["func_name"],
|
function_meta["func_name"], self.functions_mapping
|
||||||
self.functions_mapping
|
|
||||||
)
|
)
|
||||||
self.func_name = self._func.__name__
|
self.func_name = self._func.__name__
|
||||||
self._args = prepare_lazy_data(
|
self._args = prepare_lazy_data(
|
||||||
function_meta.get("args", []),
|
function_meta.get("args", []),
|
||||||
self.functions_mapping,
|
self.functions_mapping,
|
||||||
self.check_variables_set
|
self.check_variables_set,
|
||||||
)
|
)
|
||||||
self._kwargs = prepare_lazy_data(
|
self._kwargs = prepare_lazy_data(
|
||||||
function_meta.get("kwargs", {}),
|
function_meta.get("kwargs", {}),
|
||||||
self.functions_mapping,
|
self.functions_mapping,
|
||||||
self.check_variables_set
|
self.check_variables_set,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.func_name == "load_csv_file":
|
if self.func_name == "load_csv_file":
|
||||||
@@ -578,8 +576,7 @@ class LazyFunction(object):
|
|||||||
if self._kwargs:
|
if self._kwargs:
|
||||||
args_string += ", "
|
args_string += ", "
|
||||||
str_kwargs = [
|
str_kwargs = [
|
||||||
"{}={}".format(key, str(value))
|
"{}={}".format(key, str(value)) for key, value in self._kwargs.items()
|
||||||
for key, value in self._kwargs.items()
|
|
||||||
]
|
]
|
||||||
args_string += ", ".join(str_kwargs)
|
args_string += ", ".join(str_kwargs)
|
||||||
|
|
||||||
@@ -589,8 +586,8 @@ class LazyFunction(object):
|
|||||||
return self.func_name, repr(args), repr(kwargs)
|
return self.func_name, repr(args), repr(kwargs)
|
||||||
|
|
||||||
def to_value(self, variables_mapping=None):
|
def to_value(self, variables_mapping=None):
|
||||||
""" parse lazy data with evaluated variables mapping.
|
"""parse lazy data with evaluated variables mapping.
|
||||||
Notice: variables_mapping should not contain any variable or function.
|
Notice: variables_mapping should not contain any variable or function.
|
||||||
"""
|
"""
|
||||||
variables_mapping = variables_mapping or {}
|
variables_mapping = variables_mapping or {}
|
||||||
args = parse_lazy_data(self._args, variables_mapping)
|
args = parse_lazy_data(self._args, variables_mapping)
|
||||||
@@ -605,12 +602,13 @@ cached_functions_mapping = {}
|
|||||||
|
|
||||||
|
|
||||||
class LazyString(object):
|
class LazyString(object):
|
||||||
""" evaluate string lazily.
|
"""evaluate string lazily."""
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, raw_string, functions_mapping=None, check_variables_set=None, cached=False):
|
def __init__(
|
||||||
""" make raw_string as lazy object with functions_mapping
|
self, raw_string, functions_mapping=None, check_variables_set=None, cached=False
|
||||||
check if any variable undefined in check_variables_set
|
):
|
||||||
|
"""make raw_string as lazy object with functions_mapping
|
||||||
|
check if any variable undefined in check_variables_set
|
||||||
"""
|
"""
|
||||||
self.raw_string = raw_string
|
self.raw_string = raw_string
|
||||||
self.functions_mapping = functions_mapping or {}
|
self.functions_mapping = functions_mapping or {}
|
||||||
@@ -619,7 +617,7 @@ class LazyString(object):
|
|||||||
self.__parse(raw_string)
|
self.__parse(raw_string)
|
||||||
|
|
||||||
def __parse(self, raw_string):
|
def __parse(self, raw_string):
|
||||||
""" parse raw string, replace function and variable with {}
|
"""parse raw string, replace function and variable with {}
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
raw_string(str): string with functions or varialbes
|
raw_string(str): string with functions or varialbes
|
||||||
@@ -658,14 +656,10 @@ class LazyString(object):
|
|||||||
# search function like ${func($a, $b)}
|
# search function like ${func($a, $b)}
|
||||||
func_match = function_regex_compile.match(raw_string, match_start_position)
|
func_match = function_regex_compile.match(raw_string, match_start_position)
|
||||||
if func_match:
|
if func_match:
|
||||||
function_meta = {
|
function_meta = {"func_name": func_match.group(1)}
|
||||||
"func_name": func_match.group(1)
|
|
||||||
}
|
|
||||||
function_meta.update(parse_function_params(func_match.group(2)))
|
function_meta.update(parse_function_params(func_match.group(2)))
|
||||||
lazy_func = LazyFunction(
|
lazy_func = LazyFunction(
|
||||||
function_meta,
|
function_meta, self.functions_mapping, self.check_variables_set
|
||||||
self.functions_mapping,
|
|
||||||
self.check_variables_set
|
|
||||||
)
|
)
|
||||||
self._args.append(lazy_func)
|
self._args.append(lazy_func)
|
||||||
match_start_position = func_match.end()
|
match_start_position = func_match.end()
|
||||||
@@ -701,15 +695,19 @@ class LazyString(object):
|
|||||||
return "LazyString({})".format(self.raw_string)
|
return "LazyString({})".format(self.raw_string)
|
||||||
|
|
||||||
def to_value(self, variables_mapping=None):
|
def to_value(self, variables_mapping=None):
|
||||||
""" parse lazy data with evaluated variables mapping.
|
"""parse lazy data with evaluated variables mapping.
|
||||||
Notice: variables_mapping should not contain any variable or function.
|
Notice: variables_mapping should not contain any variable or function.
|
||||||
"""
|
"""
|
||||||
variables_mapping = variables_mapping or {}
|
variables_mapping = variables_mapping or {}
|
||||||
|
|
||||||
args = []
|
args = []
|
||||||
for arg in self._args:
|
for arg in self._args:
|
||||||
if isinstance(arg, LazyFunction):
|
if isinstance(arg, LazyFunction):
|
||||||
if self.cached and arg.cache_key and arg.cache_key in cached_functions_mapping:
|
if (
|
||||||
|
self.cached
|
||||||
|
and arg.cache_key
|
||||||
|
and arg.cache_key in cached_functions_mapping
|
||||||
|
):
|
||||||
value = cached_functions_mapping[arg.cache_key]
|
value = cached_functions_mapping[arg.cache_key]
|
||||||
else:
|
else:
|
||||||
value = arg.to_value(variables_mapping)
|
value = arg.to_value(variables_mapping)
|
||||||
@@ -726,8 +724,10 @@ class LazyString(object):
|
|||||||
return self._string.format(*args)
|
return self._string.format(*args)
|
||||||
|
|
||||||
|
|
||||||
def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None, cached=False):
|
def prepare_lazy_data(
|
||||||
""" make string in content as lazy object with functions_mapping
|
content, functions_mapping=None, check_variables_set=None, cached=False
|
||||||
|
):
|
||||||
|
"""make string in content as lazy object with functions_mapping
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
exceptions.VariableNotFound: if any variable undefined in check_variables_set
|
exceptions.VariableNotFound: if any variable undefined in check_variables_set
|
||||||
@@ -739,12 +739,7 @@ def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None,
|
|||||||
|
|
||||||
elif isinstance(content, (list, set, tuple)):
|
elif isinstance(content, (list, set, tuple)):
|
||||||
return [
|
return [
|
||||||
prepare_lazy_data(
|
prepare_lazy_data(item, functions_mapping, check_variables_set, cached)
|
||||||
item,
|
|
||||||
functions_mapping,
|
|
||||||
check_variables_set,
|
|
||||||
cached
|
|
||||||
)
|
|
||||||
for item in content
|
for item in content
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -752,16 +747,10 @@ def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None,
|
|||||||
parsed_content = {}
|
parsed_content = {}
|
||||||
for key, value in content.items():
|
for key, value in content.items():
|
||||||
parsed_key = prepare_lazy_data(
|
parsed_key = prepare_lazy_data(
|
||||||
key,
|
key, functions_mapping, check_variables_set, cached
|
||||||
functions_mapping,
|
|
||||||
check_variables_set,
|
|
||||||
cached
|
|
||||||
)
|
)
|
||||||
parsed_value = prepare_lazy_data(
|
parsed_value = prepare_lazy_data(
|
||||||
value,
|
value, functions_mapping, check_variables_set, cached
|
||||||
functions_mapping,
|
|
||||||
check_variables_set,
|
|
||||||
cached
|
|
||||||
)
|
)
|
||||||
parsed_content[parsed_key] = parsed_value
|
parsed_content[parsed_key] = parsed_value
|
||||||
|
|
||||||
@@ -783,8 +772,8 @@ def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None,
|
|||||||
|
|
||||||
|
|
||||||
def parse_lazy_data(content, variables_mapping=None):
|
def parse_lazy_data(content, variables_mapping=None):
|
||||||
""" parse lazy data with evaluated variables mapping.
|
"""parse lazy data with evaluated variables mapping.
|
||||||
Notice: variables_mapping should not contain any variable or function.
|
Notice: variables_mapping should not contain any variable or function.
|
||||||
"""
|
"""
|
||||||
# TODO: refactor type check
|
# TODO: refactor type check
|
||||||
if content is None or isinstance(content, (numeric_types, bool, type)):
|
if content is None or isinstance(content, (numeric_types, bool, type)):
|
||||||
@@ -795,10 +784,7 @@ def parse_lazy_data(content, variables_mapping=None):
|
|||||||
return content.to_value(variables_mapping)
|
return content.to_value(variables_mapping)
|
||||||
|
|
||||||
elif isinstance(content, (list, set, tuple)):
|
elif isinstance(content, (list, set, tuple)):
|
||||||
return [
|
return [parse_lazy_data(item, variables_mapping) for item in content]
|
||||||
parse_lazy_data(item, variables_mapping)
|
|
||||||
for item in content
|
|
||||||
]
|
|
||||||
|
|
||||||
elif isinstance(content, dict):
|
elif isinstance(content, dict):
|
||||||
parsed_content = {}
|
parsed_content = {}
|
||||||
@@ -813,24 +799,19 @@ def parse_lazy_data(content, variables_mapping=None):
|
|||||||
|
|
||||||
|
|
||||||
def eval_lazy_data(content, variables_mapping=None, functions_mapping=None):
|
def eval_lazy_data(content, variables_mapping=None, functions_mapping=None):
|
||||||
""" evaluate data instantly.
|
"""evaluate data instantly.
|
||||||
Notice: variables_mapping should not contain any variable or function.
|
Notice: variables_mapping should not contain any variable or function.
|
||||||
"""
|
"""
|
||||||
variables_mapping = variables_mapping or {}
|
variables_mapping = variables_mapping or {}
|
||||||
check_variables_set = set(variables_mapping.keys())
|
check_variables_set = set(variables_mapping.keys())
|
||||||
return parse_lazy_data(
|
return parse_lazy_data(
|
||||||
prepare_lazy_data(
|
prepare_lazy_data(content, functions_mapping, check_variables_set),
|
||||||
content,
|
variables_mapping,
|
||||||
functions_mapping,
|
|
||||||
check_variables_set
|
|
||||||
),
|
|
||||||
variables_mapping
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def extract_variables(content):
|
def extract_variables(content):
|
||||||
""" extract all variables in content recursively.
|
"""extract all variables in content recursively."""
|
||||||
"""
|
|
||||||
if isinstance(content, (list, set, tuple)):
|
if isinstance(content, (list, set, tuple)):
|
||||||
variables = set()
|
variables = set()
|
||||||
for item in content:
|
for item in content:
|
||||||
@@ -850,7 +831,7 @@ def extract_variables(content):
|
|||||||
|
|
||||||
|
|
||||||
def parse_variables_mapping(variables_mapping):
|
def parse_variables_mapping(variables_mapping):
|
||||||
""" eval each prepared variable and function in variables_mapping.
|
"""eval each prepared variable and function in variables_mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
variables_mapping (dict):
|
variables_mapping (dict):
|
||||||
@@ -911,7 +892,12 @@ def parse_variables_mapping(variables_mapping):
|
|||||||
# reference other variable, or function call with other variable
|
# reference other variable, or function call with other variable
|
||||||
# e.g. {"varA": "123$varB", "varB": "456$varC"}
|
# e.g. {"varA": "123$varB", "varB": "456$varC"}
|
||||||
# e.g. {"varC": "${sum_two($a, $b)}"}
|
# e.g. {"varC": "${sum_two($a, $b)}"}
|
||||||
if any([_var_name not in parsed_variables_mapping for _var_name in variables]):
|
if any(
|
||||||
|
[
|
||||||
|
_var_name not in parsed_variables_mapping
|
||||||
|
for _var_name in variables
|
||||||
|
]
|
||||||
|
):
|
||||||
# reference variable not parsed
|
# reference variable not parsed
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -922,7 +908,7 @@ def parse_variables_mapping(variables_mapping):
|
|||||||
|
|
||||||
|
|
||||||
def _extend_with_api(test_dict, api_def_dict):
|
def _extend_with_api(test_dict, api_def_dict):
|
||||||
""" extend test with api definition, test will merge and override api definition.
|
"""extend test with api definition, test will merge and override api definition.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
test_dict (dict): test block, this will override api_def_dict
|
test_dict (dict): test block, this will override api_def_dict
|
||||||
@@ -955,27 +941,21 @@ def _extend_with_api(test_dict, api_def_dict):
|
|||||||
# override variables
|
# override variables
|
||||||
def_variables = api_def_dict.pop("variables", [])
|
def_variables = api_def_dict.pop("variables", [])
|
||||||
test_dict["variables"] = utils.extend_variables(
|
test_dict["variables"] = utils.extend_variables(
|
||||||
def_variables,
|
def_variables, test_dict.get("variables", {})
|
||||||
test_dict.get("variables", {})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# merge & override validators TODO: relocate
|
# merge & override validators TODO: relocate
|
||||||
def_raw_validators = api_def_dict.pop("validate", [])
|
def_raw_validators = api_def_dict.pop("validate", [])
|
||||||
def_validators = [
|
def_validators = [
|
||||||
uniform_validator(_validator)
|
uniform_validator(_validator) for _validator in def_raw_validators
|
||||||
for _validator in def_raw_validators
|
|
||||||
]
|
]
|
||||||
ref_validators = test_dict.pop("validate", [])
|
ref_validators = test_dict.pop("validate", [])
|
||||||
test_dict["validate"] = extend_validators(
|
test_dict["validate"] = extend_validators(def_validators, ref_validators)
|
||||||
def_validators,
|
|
||||||
ref_validators
|
|
||||||
)
|
|
||||||
|
|
||||||
# merge & override extractors
|
# merge & override extractors
|
||||||
def_extrators = api_def_dict.pop("extract", {})
|
def_extrators = api_def_dict.pop("extract", {})
|
||||||
test_dict["extract"] = utils.extend_variables(
|
test_dict["extract"] = utils.extend_variables(
|
||||||
def_extrators,
|
def_extrators, test_dict.get("extract", {})
|
||||||
test_dict.get("extract", {})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# merge & override request
|
# merge & override request
|
||||||
@@ -1010,7 +990,7 @@ def _extend_with_api(test_dict, api_def_dict):
|
|||||||
|
|
||||||
|
|
||||||
def _extend_with_testcase(test_dict, testcase_def_dict):
|
def _extend_with_testcase(test_dict, testcase_def_dict):
|
||||||
""" extend test with testcase definition
|
"""extend test with testcase definition
|
||||||
test will merge and override testcase config definition.
|
test will merge and override testcase config definition.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1024,7 +1004,8 @@ def _extend_with_testcase(test_dict, testcase_def_dict):
|
|||||||
# override testcase config variables
|
# override testcase config variables
|
||||||
testcase_def_dict["config"].setdefault("variables", {})
|
testcase_def_dict["config"].setdefault("variables", {})
|
||||||
testcase_def_variables = utils.ensure_mapping_format(
|
testcase_def_variables = utils.ensure_mapping_format(
|
||||||
testcase_def_dict["config"].get("variables", {}))
|
testcase_def_dict["config"].get("variables", {})
|
||||||
|
)
|
||||||
testcase_def_variables.update(test_dict.pop("variables", {}))
|
testcase_def_variables.update(test_dict.pop("variables", {}))
|
||||||
testcase_def_dict["config"]["variables"] = testcase_def_variables
|
testcase_def_dict["config"]["variables"] = testcase_def_variables
|
||||||
|
|
||||||
@@ -1035,9 +1016,11 @@ def _extend_with_testcase(test_dict, testcase_def_dict):
|
|||||||
testcase_def_dict["config"]["base_url"] = test_base_url
|
testcase_def_dict["config"]["base_url"] = test_base_url
|
||||||
|
|
||||||
# override name
|
# override name
|
||||||
test_name = test_dict.pop("name", None) \
|
test_name = (
|
||||||
or testcase_def_dict["config"].pop("name", None) \
|
test_dict.pop("name", None)
|
||||||
or "testcase name undefined"
|
or testcase_def_dict["config"].pop("name", None)
|
||||||
|
or "testcase name undefined"
|
||||||
|
)
|
||||||
|
|
||||||
# override testcase config name, output, etc.
|
# override testcase config name, output, etc.
|
||||||
testcase_def_dict["config"].update(test_dict)
|
testcase_def_dict["config"].update(test_dict)
|
||||||
@@ -1048,8 +1031,7 @@ def _extend_with_testcase(test_dict, testcase_def_dict):
|
|||||||
|
|
||||||
|
|
||||||
def __prepare_config(config, project_mapping, session_variables_set=None):
|
def __prepare_config(config, project_mapping, session_variables_set=None):
|
||||||
""" parse testcase/testsuite config.
|
"""parse testcase/testsuite config."""
|
||||||
"""
|
|
||||||
# get config variables
|
# get config variables
|
||||||
raw_config_variables = config.pop("variables", {})
|
raw_config_variables = config.pop("variables", {})
|
||||||
|
|
||||||
@@ -1057,7 +1039,8 @@ def __prepare_config(config, project_mapping, session_variables_set=None):
|
|||||||
functions = project_mapping.get("functions", {})
|
functions = project_mapping.get("functions", {})
|
||||||
|
|
||||||
if isinstance(raw_config_variables, basestring) and function_regex_compile.match(
|
if isinstance(raw_config_variables, basestring) and function_regex_compile.match(
|
||||||
raw_config_variables):
|
raw_config_variables
|
||||||
|
):
|
||||||
# config variables are generated by calling function
|
# config variables are generated by calling function
|
||||||
# e.g.
|
# e.g.
|
||||||
# "config": {
|
# "config": {
|
||||||
@@ -1077,13 +1060,17 @@ def __prepare_config(config, project_mapping, session_variables_set=None):
|
|||||||
config["variables"] = raw_config_variables_mapping
|
config["variables"] = raw_config_variables_mapping
|
||||||
|
|
||||||
check_variables_set = set(raw_config_variables_mapping.keys())
|
check_variables_set = set(raw_config_variables_mapping.keys())
|
||||||
check_variables_set |= (session_variables_set or set())
|
check_variables_set |= session_variables_set or set()
|
||||||
prepared_config = prepare_lazy_data(config, functions, check_variables_set, cached=True)
|
prepared_config = prepare_lazy_data(
|
||||||
|
config, functions, check_variables_set, cached=True
|
||||||
|
)
|
||||||
return prepared_config
|
return prepared_config
|
||||||
|
|
||||||
|
|
||||||
def __prepare_testcase_tests(tests, config, project_mapping, session_variables_set=None):
|
def __prepare_testcase_tests(
|
||||||
""" override tests with testcase config variables, base_url and verify.
|
tests, config, project_mapping, session_variables_set=None
|
||||||
|
):
|
||||||
|
"""override tests with testcase config variables, base_url and verify.
|
||||||
test maybe nested testcase.
|
test maybe nested testcase.
|
||||||
|
|
||||||
variables priority:
|
variables priority:
|
||||||
@@ -1107,7 +1094,9 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
functions = project_mapping.get("functions", {})
|
functions = project_mapping.get("functions", {})
|
||||||
|
|
||||||
prepared_testcase_tests = []
|
prepared_testcase_tests = []
|
||||||
session_variables_set = set(config_variables.keys()) | (session_variables_set or set())
|
session_variables_set = set(config_variables.keys()) | (
|
||||||
|
session_variables_set or set()
|
||||||
|
)
|
||||||
for test_dict in tests:
|
for test_dict in tests:
|
||||||
|
|
||||||
teststep_variables_set = {"request", "response"}
|
teststep_variables_set = {"request", "response"}
|
||||||
@@ -1115,8 +1104,7 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
# 1, testcase config => testcase tests
|
# 1, testcase config => testcase tests
|
||||||
# override test_dict variables
|
# override test_dict variables
|
||||||
test_dict_variables = utils.extend_variables(
|
test_dict_variables = utils.extend_variables(
|
||||||
test_dict.pop("variables", {}),
|
test_dict.pop("variables", {}), config_variables
|
||||||
config_variables
|
|
||||||
)
|
)
|
||||||
test_dict["variables"] = test_dict_variables
|
test_dict["variables"] = test_dict_variables
|
||||||
|
|
||||||
@@ -1128,8 +1116,7 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
if "validate" in test_dict:
|
if "validate" in test_dict:
|
||||||
ref_raw_validators = test_dict.pop("validate", [])
|
ref_raw_validators = test_dict.pop("validate", [])
|
||||||
test_dict["validate"] = [
|
test_dict["validate"] = [
|
||||||
uniform_validator(_validator)
|
uniform_validator(_validator) for _validator in ref_raw_validators
|
||||||
for _validator in ref_raw_validators
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if "testcase_def" in test_dict:
|
if "testcase_def" in test_dict:
|
||||||
@@ -1152,7 +1139,9 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
test_dict["config"].setdefault("verify", config_verify)
|
test_dict["config"].setdefault("verify", config_verify)
|
||||||
|
|
||||||
# 3, testcase_def config => testcase_def test_dict
|
# 3, testcase_def config => testcase_def test_dict
|
||||||
test_dict = _parse_testcase(test_dict, project_mapping, session_variables_set)
|
test_dict = _parse_testcase(
|
||||||
|
test_dict, project_mapping, session_variables_set
|
||||||
|
)
|
||||||
if not test_dict:
|
if not test_dict:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1169,6 +1158,7 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
|
|
||||||
if "upload" in test_dict["request"]:
|
if "upload" in test_dict["request"]:
|
||||||
from httprunner.ext.uploader import prepare_upload_test
|
from httprunner.ext.uploader import prepare_upload_test
|
||||||
|
|
||||||
prepare_upload_test(test_dict)
|
prepare_upload_test(test_dict)
|
||||||
|
|
||||||
# current teststep variables
|
# current teststep variables
|
||||||
@@ -1187,27 +1177,18 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
for _validator in validators:
|
for _validator in validators:
|
||||||
function_meta = {
|
function_meta = {
|
||||||
"func_name": _validator["comparator"],
|
"func_name": _validator["comparator"],
|
||||||
"args": [
|
"args": [_validator["check"], _validator["expect"]],
|
||||||
_validator["check"],
|
"kwargs": {},
|
||||||
_validator["expect"]
|
|
||||||
],
|
|
||||||
"kwargs": {}
|
|
||||||
}
|
}
|
||||||
prepared_validators.append(
|
prepared_validators.append(
|
||||||
LazyFunction(
|
LazyFunction(function_meta, functions, teststep_variables_set)
|
||||||
function_meta,
|
|
||||||
functions,
|
|
||||||
teststep_variables_set
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
test_dict["validate"] = prepared_validators
|
test_dict["validate"] = prepared_validators
|
||||||
|
|
||||||
# convert variables and functions to lazy object.
|
# convert variables and functions to lazy object.
|
||||||
# raises VariableNotFound if undefined variable exists in test_dict
|
# raises VariableNotFound if undefined variable exists in test_dict
|
||||||
prepared_test_dict = prepare_lazy_data(
|
prepared_test_dict = prepare_lazy_data(
|
||||||
test_dict,
|
test_dict, functions, teststep_variables_set
|
||||||
functions,
|
|
||||||
teststep_variables_set
|
|
||||||
)
|
)
|
||||||
prepared_testcase_tests.append(prepared_test_dict)
|
prepared_testcase_tests.append(prepared_test_dict)
|
||||||
|
|
||||||
@@ -1215,7 +1196,7 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
|
|
||||||
|
|
||||||
def _parse_testcase(testcase, project_mapping, session_variables_set=None):
|
def _parse_testcase(testcase, project_mapping, session_variables_set=None):
|
||||||
""" parse testcase
|
"""parse testcase
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
testcase (dict):
|
testcase (dict):
|
||||||
@@ -1229,25 +1210,22 @@ def _parse_testcase(testcase, project_mapping, session_variables_set=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
prepared_config = __prepare_config(
|
prepared_config = __prepare_config(
|
||||||
testcase["config"],
|
testcase["config"], project_mapping, session_variables_set
|
||||||
project_mapping,
|
|
||||||
session_variables_set
|
|
||||||
)
|
)
|
||||||
prepared_testcase_tests = __prepare_testcase_tests(
|
prepared_testcase_tests = __prepare_testcase_tests(
|
||||||
testcase["teststeps"],
|
testcase["teststeps"],
|
||||||
prepared_config,
|
prepared_config,
|
||||||
project_mapping,
|
project_mapping,
|
||||||
session_variables_set
|
session_variables_set,
|
||||||
)
|
)
|
||||||
return {
|
return {"config": prepared_config, "teststeps": prepared_testcase_tests}
|
||||||
"config": prepared_config,
|
|
||||||
"teststeps": prepared_testcase_tests
|
|
||||||
}
|
|
||||||
except (exceptions.MyBaseFailure, exceptions.MyBaseError) as ex:
|
except (exceptions.MyBaseFailure, exceptions.MyBaseError) as ex:
|
||||||
testcase_type = testcase["type"]
|
testcase_type = testcase["type"]
|
||||||
testcase_path = testcase.get("path")
|
testcase_path = testcase.get("path")
|
||||||
|
|
||||||
logger.log_error("failed to parse testcase: {}, error: {}".format(testcase_path, ex))
|
logger.log_error(
|
||||||
|
"failed to parse testcase: {}, error: {}".format(testcase_path, ex)
|
||||||
|
)
|
||||||
|
|
||||||
global parse_failed_testfiles
|
global parse_failed_testfiles
|
||||||
if testcase_type not in parse_failed_testfiles:
|
if testcase_type not in parse_failed_testfiles:
|
||||||
@@ -1259,7 +1237,7 @@ def _parse_testcase(testcase, project_mapping, session_variables_set=None):
|
|||||||
|
|
||||||
|
|
||||||
def __get_parsed_testsuite_testcases(testcases, testsuite_config, project_mapping):
|
def __get_parsed_testsuite_testcases(testcases, testsuite_config, project_mapping):
|
||||||
""" override testscases with testsuite config variables, base_url and verify.
|
"""override testscases with testsuite config variables, base_url and verify.
|
||||||
|
|
||||||
variables priority:
|
variables priority:
|
||||||
parameters > testsuite config > testcase config > testcase_def config > testcase_def tests > api
|
parameters > testsuite config > testcase config > testcase_def config > testcase_def tests > api
|
||||||
@@ -1323,45 +1301,46 @@ def __get_parsed_testsuite_testcases(testcases, testsuite_config, project_mappin
|
|||||||
# 1, testsuite config => testcase config
|
# 1, testsuite config => testcase config
|
||||||
# override test_dict variables
|
# override test_dict variables
|
||||||
testcase_config_variables = utils.extend_variables(
|
testcase_config_variables = utils.extend_variables(
|
||||||
testcase.pop("variables", {}),
|
testcase.pop("variables", {}), testsuite_config_variables
|
||||||
testsuite_config_variables
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2, testcase config > testcase_def config
|
# 2, testcase config > testcase_def config
|
||||||
# override testcase_def config variables
|
# override testcase_def config variables
|
||||||
overrided_testcase_config_variables = utils.extend_variables(
|
overrided_testcase_config_variables = utils.extend_variables(
|
||||||
parsed_testcase["config"].pop("variables", {}),
|
parsed_testcase["config"].pop("variables", {}), testcase_config_variables
|
||||||
testcase_config_variables
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if overrided_testcase_config_variables:
|
if overrided_testcase_config_variables:
|
||||||
parsed_testcase["config"]["variables"] = overrided_testcase_config_variables
|
parsed_testcase["config"]["variables"] = overrided_testcase_config_variables
|
||||||
|
|
||||||
# parse config variables
|
# parse config variables
|
||||||
parsed_config_variables = parse_variables_mapping(overrided_testcase_config_variables)
|
parsed_config_variables = parse_variables_mapping(
|
||||||
|
overrided_testcase_config_variables
|
||||||
|
)
|
||||||
|
|
||||||
# parse parameters
|
# parse parameters
|
||||||
if "parameters" in testcase and testcase["parameters"]:
|
if "parameters" in testcase and testcase["parameters"]:
|
||||||
cartesian_product_parameters = parse_parameters(
|
cartesian_product_parameters = parse_parameters(
|
||||||
testcase["parameters"],
|
testcase["parameters"], parsed_config_variables, functions
|
||||||
parsed_config_variables,
|
|
||||||
functions
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for parameter_variables in cartesian_product_parameters:
|
for parameter_variables in cartesian_product_parameters:
|
||||||
# deepcopy to avoid influence between parameters
|
# deepcopy to avoid influence between parameters
|
||||||
testcase_copied = utils.deepcopy_dict(parsed_testcase)
|
testcase_copied = utils.deepcopy_dict(parsed_testcase)
|
||||||
parsed_config_variables_copied = utils.deepcopy_dict(parsed_config_variables)
|
parsed_config_variables_copied = utils.deepcopy_dict(
|
||||||
testcase_copied["config"]["variables"] = utils.extend_variables(
|
parsed_config_variables
|
||||||
parsed_config_variables_copied,
|
)
|
||||||
parameter_variables
|
testcase_copied["config"]["variables"] = utils.extend_variables(
|
||||||
|
parsed_config_variables_copied, parameter_variables
|
||||||
|
)
|
||||||
|
parsed_testcase_copied = _parse_testcase(
|
||||||
|
testcase_copied, project_mapping
|
||||||
)
|
)
|
||||||
parsed_testcase_copied = _parse_testcase(testcase_copied, project_mapping)
|
|
||||||
if not parsed_testcase_copied:
|
if not parsed_testcase_copied:
|
||||||
continue
|
continue
|
||||||
parsed_testcase_copied["config"]["name"] = parse_lazy_data(
|
parsed_testcase_copied["config"]["name"] = parse_lazy_data(
|
||||||
parsed_testcase_copied["config"]["name"],
|
parsed_testcase_copied["config"]["name"],
|
||||||
testcase_copied["config"]["variables"]
|
testcase_copied["config"]["variables"],
|
||||||
)
|
)
|
||||||
parsed_testcase_list.append(parsed_testcase_copied)
|
parsed_testcase_list.append(parsed_testcase_copied)
|
||||||
|
|
||||||
@@ -1378,15 +1357,13 @@ def _parse_testsuite(testsuite, project_mapping):
|
|||||||
testsuite.setdefault("config", {})
|
testsuite.setdefault("config", {})
|
||||||
prepared_config = __prepare_config(testsuite["config"], project_mapping)
|
prepared_config = __prepare_config(testsuite["config"], project_mapping)
|
||||||
parsed_testcase_list = __get_parsed_testsuite_testcases(
|
parsed_testcase_list = __get_parsed_testsuite_testcases(
|
||||||
testsuite["testcases"],
|
testsuite["testcases"], prepared_config, project_mapping
|
||||||
prepared_config,
|
|
||||||
project_mapping
|
|
||||||
)
|
)
|
||||||
return parsed_testcase_list
|
return parsed_testcase_list
|
||||||
|
|
||||||
|
|
||||||
def parse_tests(tests_mapping):
|
def parse_tests(tests_mapping):
|
||||||
""" parse tests and load to parsed testcases
|
"""parse tests and load to parsed testcases
|
||||||
tests include api, testcases and testsuites.
|
tests include api, testcases and testsuites.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1475,12 +1452,10 @@ def parse_tests(tests_mapping):
|
|||||||
# encapsulate api as a testcase
|
# encapsulate api as a testcase
|
||||||
for api_content in tests_mapping["apis"]:
|
for api_content in tests_mapping["apis"]:
|
||||||
testcase = {
|
testcase = {
|
||||||
"config": {
|
"config": {"name": api_content.get("name")},
|
||||||
"name": api_content.get("name")
|
|
||||||
},
|
|
||||||
"teststeps": [api_content],
|
"teststeps": [api_content],
|
||||||
"path": api_content.pop("path", None),
|
"path": api_content.pop("path", None),
|
||||||
"type": api_content.pop("type", "api")
|
"type": api_content.pop("type", "api"),
|
||||||
}
|
}
|
||||||
parsed_testcase = _parse_testcase(testcase, project_mapping)
|
parsed_testcase = _parse_testcase(testcase, project_mapping)
|
||||||
if not parsed_testcase:
|
if not parsed_testcase:
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ __all__ = [
|
|||||||
"get_summary",
|
"get_summary",
|
||||||
"stringify_summary",
|
"stringify_summary",
|
||||||
"HtmlTestResult",
|
"HtmlTestResult",
|
||||||
"gen_html_report"
|
"gen_html_report",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -9,7 +9,4 @@ HttpRunner html report
|
|||||||
from httprunner.report.html.result import HtmlTestResult
|
from httprunner.report.html.result import HtmlTestResult
|
||||||
from httprunner.report.html.gen_report import gen_html_report
|
from httprunner.report.html.gen_report import gen_html_report
|
||||||
|
|
||||||
__all__ = [
|
__all__ = ["HtmlTestResult", "gen_html_report"]
|
||||||
"HtmlTestResult",
|
|
||||||
"gen_html_report"
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from httprunner.exceptions import SummaryEmpty
|
|||||||
|
|
||||||
|
|
||||||
def gen_html_report(summary, report_template=None, report_dir=None, report_file=None):
|
def gen_html_report(summary, report_template=None, report_dir=None, report_file=None):
|
||||||
""" render html report with specified report name and template
|
"""render html report with specified report name and template
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
summary (dict): test result summary data
|
summary (dict): test result summary data
|
||||||
@@ -24,8 +24,7 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
|
|||||||
|
|
||||||
if not report_template:
|
if not report_template:
|
||||||
report_template = os.path.join(
|
report_template = os.path.join(
|
||||||
os.path.abspath(os.path.dirname(__file__)),
|
os.path.abspath(os.path.dirname(__file__)), "template.html"
|
||||||
"template.html"
|
|
||||||
)
|
)
|
||||||
logger.log_debug("No html report template specified, use default.")
|
logger.log_debug("No html report template specified, use default.")
|
||||||
else:
|
else:
|
||||||
@@ -43,22 +42,22 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
|
|||||||
else:
|
else:
|
||||||
report_dir = report_dir or os.path.join(os.getcwd(), "reports")
|
report_dir = report_dir or os.path.join(os.getcwd(), "reports")
|
||||||
# fix #826: Windows does not support file name include ":"
|
# fix #826: Windows does not support file name include ":"
|
||||||
report_file_name = "{}.html".format(utc_time_iso_8601_str.replace(":", "").replace("-", ""))
|
report_file_name = "{}.html".format(
|
||||||
|
utc_time_iso_8601_str.replace(":", "").replace("-", "")
|
||||||
|
)
|
||||||
|
|
||||||
if not os.path.isdir(report_dir):
|
if not os.path.isdir(report_dir):
|
||||||
os.makedirs(report_dir)
|
os.makedirs(report_dir)
|
||||||
|
|
||||||
report_path = os.path.join(report_dir, report_file_name)
|
report_path = os.path.join(report_dir, report_file_name)
|
||||||
with io.open(report_template, "r", encoding='utf-8') as fp_r:
|
with io.open(report_template, "r", encoding="utf-8") as fp_r:
|
||||||
template_content = fp_r.read()
|
template_content = fp_r.read()
|
||||||
with io.open(report_path, 'w', encoding='utf-8') as fp_w:
|
with io.open(report_path, "w", encoding="utf-8") as fp_w:
|
||||||
rendered_content = Template(
|
rendered_content = Template(
|
||||||
template_content,
|
template_content, extensions=["jinja2.ext.loopcontrols"]
|
||||||
extensions=["jinja2.ext.loopcontrols"]
|
|
||||||
).render(summary)
|
).render(summary)
|
||||||
fp_w.write(rendered_content)
|
fp_w.write(rendered_content)
|
||||||
|
|
||||||
logger.log_info("Generated Html report: {}".format(report_path))
|
logger.log_info("Generated Html report: {}".format(report_path))
|
||||||
|
|
||||||
return report_path
|
return report_path
|
||||||
|
|
||||||
|
|||||||
@@ -5,19 +5,20 @@ from httprunner import logger
|
|||||||
|
|
||||||
|
|
||||||
class HtmlTestResult(unittest.TextTestResult):
|
class HtmlTestResult(unittest.TextTestResult):
|
||||||
""" A html result class that can generate formatted html results.
|
"""A html result class that can generate formatted html results.
|
||||||
Used by TextTestRunner.
|
Used by TextTestRunner.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, stream, descriptions, verbosity):
|
def __init__(self, stream, descriptions, verbosity):
|
||||||
super(HtmlTestResult, self).__init__(stream, descriptions, verbosity)
|
super(HtmlTestResult, self).__init__(stream, descriptions, verbosity)
|
||||||
self.records = []
|
self.records = []
|
||||||
|
|
||||||
def _record_test(self, test, status, attachment=''):
|
def _record_test(self, test, status, attachment=""):
|
||||||
data = {
|
data = {
|
||||||
'name': test.shortDescription(),
|
"name": test.shortDescription(),
|
||||||
'status': status,
|
"status": status,
|
||||||
'attachment': attachment,
|
"attachment": attachment,
|
||||||
"meta_datas": test.meta_datas
|
"meta_datas": test.meta_datas,
|
||||||
}
|
}
|
||||||
self.records.append(data)
|
self.records.append(data)
|
||||||
|
|
||||||
@@ -25,38 +26,38 @@ class HtmlTestResult(unittest.TextTestResult):
|
|||||||
self.start_at = time.time()
|
self.start_at = time.time()
|
||||||
|
|
||||||
def startTest(self, test):
|
def startTest(self, test):
|
||||||
""" add start test time """
|
"""add start test time"""
|
||||||
super(HtmlTestResult, self).startTest(test)
|
super(HtmlTestResult, self).startTest(test)
|
||||||
logger.color_print(test.shortDescription(), "yellow")
|
logger.color_print(test.shortDescription(), "yellow")
|
||||||
|
|
||||||
def addSuccess(self, test):
|
def addSuccess(self, test):
|
||||||
super(HtmlTestResult, self).addSuccess(test)
|
super(HtmlTestResult, self).addSuccess(test)
|
||||||
self._record_test(test, 'success')
|
self._record_test(test, "success")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
def addError(self, test, err):
|
def addError(self, test, err):
|
||||||
super(HtmlTestResult, self).addError(test, err)
|
super(HtmlTestResult, self).addError(test, err)
|
||||||
self._record_test(test, 'error', self._exc_info_to_string(err, test))
|
self._record_test(test, "error", self._exc_info_to_string(err, test))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
def addFailure(self, test, err):
|
def addFailure(self, test, err):
|
||||||
super(HtmlTestResult, self).addFailure(test, err)
|
super(HtmlTestResult, self).addFailure(test, err)
|
||||||
self._record_test(test, 'failure', self._exc_info_to_string(err, test))
|
self._record_test(test, "failure", self._exc_info_to_string(err, test))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
def addSkip(self, test, reason):
|
def addSkip(self, test, reason):
|
||||||
super(HtmlTestResult, self).addSkip(test, reason)
|
super(HtmlTestResult, self).addSkip(test, reason)
|
||||||
self._record_test(test, 'skipped', reason)
|
self._record_test(test, "skipped", reason)
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
def addExpectedFailure(self, test, err):
|
def addExpectedFailure(self, test, err):
|
||||||
super(HtmlTestResult, self).addExpectedFailure(test, err)
|
super(HtmlTestResult, self).addExpectedFailure(test, err)
|
||||||
self._record_test(test, 'ExpectedFailure', self._exc_info_to_string(err, test))
|
self._record_test(test, "ExpectedFailure", self._exc_info_to_string(err, test))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
def addUnexpectedSuccess(self, test):
|
def addUnexpectedSuccess(self, test):
|
||||||
super(HtmlTestResult, self).addUnexpectedSuccess(test)
|
super(HtmlTestResult, self).addUnexpectedSuccess(test)
|
||||||
self._record_test(test, 'UnexpectedSuccess')
|
self._record_test(test, "UnexpectedSuccess")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -9,43 +9,37 @@ from httprunner import __version__
|
|||||||
|
|
||||||
|
|
||||||
def prepare_event_kwargs(event_name, params):
|
def prepare_event_kwargs(event_name, params):
|
||||||
""" prepare report event kwargs"""
|
"""prepare report event kwargs"""
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"headers": {
|
"headers": {"content-type": "application/json"},
|
||||||
'content-type': 'application/json'
|
|
||||||
},
|
|
||||||
"json": {
|
"json": {
|
||||||
"user": {
|
"user": {"user_unique_id": str(uuid.getnode())},
|
||||||
"user_unique_id": str(uuid.getnode())
|
|
||||||
},
|
|
||||||
"header": {
|
"header": {
|
||||||
"app_id": 173519,
|
"app_id": 173519,
|
||||||
"os_name": platform.system(),
|
"os_name": platform.system(),
|
||||||
"os_version": platform.release(),
|
"os_version": platform.release(),
|
||||||
"app_version": __version__ # HttpRunner version
|
"app_version": __version__, # HttpRunner version
|
||||||
},
|
},
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"event": event_name,
|
"event": event_name,
|
||||||
"params": json.dumps(params),
|
"params": json.dumps(params),
|
||||||
"time": int(time.time())
|
"time": int(time.time()),
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"verbose": 1
|
"verbose": 1,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
def report_event(event_name, success=True):
|
def report_event(event_name, success=True):
|
||||||
params = {
|
params = {"success": 1 if success else 0}
|
||||||
"success": 1 if success else 0
|
|
||||||
}
|
|
||||||
kwargs = prepare_event_kwargs(event_name, params)
|
kwargs = prepare_event_kwargs(event_name, params)
|
||||||
resp = requests.post("http://mcs.snssdk.com/v1/json", **kwargs)
|
resp = requests.post("http://mcs.snssdk.com/v1/json", **kwargs)
|
||||||
print("resp---", resp.json())
|
print("resp---", resp.json())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
report_event("loader")
|
report_event("loader")
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from httprunner.compat import basestring, bytes, json, numeric_types, JSONDecode
|
|||||||
|
|
||||||
|
|
||||||
def dumps_json(value):
|
def dumps_json(value):
|
||||||
""" dumps json value to indented string
|
"""dumps json value to indented string
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (dict): raw json data
|
value (dict): raw json data
|
||||||
@@ -28,7 +28,7 @@ def detect_encoding(value):
|
|||||||
|
|
||||||
|
|
||||||
def __stringify_request(request_data):
|
def __stringify_request(request_data):
|
||||||
""" stringfy HTTP request data
|
"""stringfy HTTP request data
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request_data (dict): HTTP request data in dict.
|
request_data (dict): HTTP request data in dict.
|
||||||
@@ -84,7 +84,7 @@ def __stringify_request(request_data):
|
|||||||
|
|
||||||
|
|
||||||
def __stringify_response(response_data):
|
def __stringify_response(response_data):
|
||||||
""" stringfy HTTP response data
|
"""stringfy HTTP response data
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
response_data (dict):
|
response_data (dict):
|
||||||
@@ -124,8 +124,7 @@ def __stringify_response(response_data):
|
|||||||
if key == "body" and "image" in response_data["content_type"]:
|
if key == "body" and "image" in response_data["content_type"]:
|
||||||
# display image
|
# display image
|
||||||
value = "data:{};base64,{}".format(
|
value = "data:{};base64,{}".format(
|
||||||
response_data["content_type"],
|
response_data["content_type"], b64encode(value).decode(encoding)
|
||||||
b64encode(value).decode(encoding)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
value = escape(value.decode(encoding))
|
value = escape(value.decode(encoding))
|
||||||
@@ -143,7 +142,7 @@ def __stringify_response(response_data):
|
|||||||
|
|
||||||
|
|
||||||
def __expand_meta_datas(meta_datas, meta_datas_expanded):
|
def __expand_meta_datas(meta_datas, meta_datas_expanded):
|
||||||
""" expand meta_datas to one level
|
"""expand meta_datas to one level
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
meta_datas (dict/list): maybe in nested format
|
meta_datas (dict/list): maybe in nested format
|
||||||
@@ -173,8 +172,7 @@ def __expand_meta_datas(meta_datas, meta_datas_expanded):
|
|||||||
|
|
||||||
|
|
||||||
def __get_total_response_time(meta_datas_expanded):
|
def __get_total_response_time(meta_datas_expanded):
|
||||||
""" caculate total response time of all meta_datas
|
"""caculate total response time of all meta_datas"""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
response_time = 0
|
response_time = 0
|
||||||
for meta_data in meta_datas_expanded:
|
for meta_data in meta_datas_expanded:
|
||||||
@@ -200,15 +198,14 @@ def __stringify_meta_datas(meta_datas):
|
|||||||
|
|
||||||
|
|
||||||
def stringify_summary(summary):
|
def stringify_summary(summary):
|
||||||
""" stringify summary, in order to dump json file and generate html report.
|
"""stringify summary, in order to dump json file and generate html report."""
|
||||||
"""
|
|
||||||
for index, suite_summary in enumerate(summary["details"]):
|
for index, suite_summary in enumerate(summary["details"]):
|
||||||
|
|
||||||
if not suite_summary.get("name"):
|
if not suite_summary.get("name"):
|
||||||
suite_summary["name"] = "testcase {}".format(index)
|
suite_summary["name"] = "testcase {}".format(index)
|
||||||
|
|
||||||
for record in suite_summary.get("records"):
|
for record in suite_summary.get("records"):
|
||||||
meta_datas = record['meta_datas']
|
meta_datas = record["meta_datas"]
|
||||||
__stringify_meta_datas(meta_datas)
|
__stringify_meta_datas(meta_datas)
|
||||||
meta_datas_expanded = []
|
meta_datas_expanded = []
|
||||||
__expand_meta_datas(meta_datas, meta_datas_expanded)
|
__expand_meta_datas(meta_datas, meta_datas_expanded)
|
||||||
|
|||||||
@@ -7,15 +7,14 @@ def get_platform():
|
|||||||
return {
|
return {
|
||||||
"httprunner_version": __version__,
|
"httprunner_version": __version__,
|
||||||
"python_version": "{} {}".format(
|
"python_version": "{} {}".format(
|
||||||
platform.python_implementation(),
|
platform.python_implementation(), platform.python_version()
|
||||||
platform.python_version()
|
|
||||||
),
|
),
|
||||||
"platform": platform.platform()
|
"platform": platform.platform(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def aggregate_stat(origin_stat, new_stat):
|
def aggregate_stat(origin_stat, new_stat):
|
||||||
""" aggregate new_stat to origin_stat.
|
"""aggregate new_stat to origin_stat.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
origin_stat (dict): origin stat dict, will be updated with new_stat dict.
|
origin_stat (dict): origin stat dict, will be updated with new_stat dict.
|
||||||
@@ -30,8 +29,10 @@ def aggregate_stat(origin_stat, new_stat):
|
|||||||
origin_stat["start_at"] = min(origin_stat["start_at"], new_stat["start_at"])
|
origin_stat["start_at"] = min(origin_stat["start_at"], new_stat["start_at"])
|
||||||
elif key == "duration":
|
elif key == "duration":
|
||||||
# duration = max_end_time - min_start_time
|
# duration = max_end_time - min_start_time
|
||||||
max_end_time = max(origin_stat["start_at"] + origin_stat["duration"],
|
max_end_time = max(
|
||||||
new_stat["start_at"] + new_stat["duration"])
|
origin_stat["start_at"] + origin_stat["duration"],
|
||||||
|
new_stat["start_at"] + new_stat["duration"],
|
||||||
|
)
|
||||||
min_start_time = min(origin_stat["start_at"], new_stat["start_at"])
|
min_start_time = min(origin_stat["start_at"], new_stat["start_at"])
|
||||||
origin_stat["duration"] = max_end_time - min_start_time
|
origin_stat["duration"] = max_end_time - min_start_time
|
||||||
else:
|
else:
|
||||||
@@ -39,7 +40,7 @@ def aggregate_stat(origin_stat, new_stat):
|
|||||||
|
|
||||||
|
|
||||||
def get_summary(result):
|
def get_summary(result):
|
||||||
""" get summary from test result
|
"""get summary from test result
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
result (instance): HtmlTestResult() instance
|
result (instance): HtmlTestResult() instance
|
||||||
@@ -58,25 +59,24 @@ def get_summary(result):
|
|||||||
summary = {
|
summary = {
|
||||||
"success": result.wasSuccessful(),
|
"success": result.wasSuccessful(),
|
||||||
"stat": {
|
"stat": {
|
||||||
'total': result.testsRun,
|
"total": result.testsRun,
|
||||||
'failures': len(result.failures),
|
"failures": len(result.failures),
|
||||||
'errors': len(result.errors),
|
"errors": len(result.errors),
|
||||||
'skipped': len(result.skipped),
|
"skipped": len(result.skipped),
|
||||||
'expectedFailures': len(result.expectedFailures),
|
"expectedFailures": len(result.expectedFailures),
|
||||||
'unexpectedSuccesses': len(result.unexpectedSuccesses)
|
"unexpectedSuccesses": len(result.unexpectedSuccesses),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
summary["stat"]["successes"] = summary["stat"]["total"] \
|
summary["stat"]["successes"] = (
|
||||||
- summary["stat"]["failures"] \
|
summary["stat"]["total"]
|
||||||
- summary["stat"]["errors"] \
|
- summary["stat"]["failures"]
|
||||||
- summary["stat"]["skipped"] \
|
- summary["stat"]["errors"]
|
||||||
- summary["stat"]["expectedFailures"] \
|
- summary["stat"]["skipped"]
|
||||||
- summary["stat"]["unexpectedSuccesses"]
|
- summary["stat"]["expectedFailures"]
|
||||||
|
- summary["stat"]["unexpectedSuccesses"]
|
||||||
|
)
|
||||||
|
|
||||||
summary["time"] = {
|
summary["time"] = {"start_at": result.start_at, "duration": result.duration}
|
||||||
'start_at': result.start_at,
|
|
||||||
'duration': result.duration
|
|
||||||
}
|
|
||||||
summary["records"] = result.records
|
summary["records"] = result.records
|
||||||
|
|
||||||
return summary
|
return summary
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ text_extractor_regexp_compile = re.compile(r".*\(.*\).*")
|
|||||||
|
|
||||||
|
|
||||||
class ResponseObject(object):
|
class ResponseObject(object):
|
||||||
|
|
||||||
def __init__(self, resp_obj):
|
def __init__(self, resp_obj):
|
||||||
""" initialize with a requests.Response object
|
"""initialize with a requests.Response object
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
resp_obj (instance): requests.Response instance
|
resp_obj (instance): requests.Response instance
|
||||||
@@ -64,9 +63,9 @@ class ResponseObject(object):
|
|||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
raise exceptions.ExtractFailure("\tjsonpath {} get nothing\n".format(field))
|
raise exceptions.ExtractFailure("\tjsonpath {} get nothing\n".format(field))
|
||||||
|
|
||||||
def _extract_field_with_regex(self, field):
|
def _extract_field_with_regex(self, field):
|
||||||
""" extract field from response content with regex.
|
"""extract field from response content with regex.
|
||||||
requests.Response body could be json or html text.
|
requests.Response body could be json or html text.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -87,15 +86,15 @@ class ResponseObject(object):
|
|||||||
"""
|
"""
|
||||||
matched = re.search(field, self.text)
|
matched = re.search(field, self.text)
|
||||||
if not matched:
|
if not matched:
|
||||||
err_msg = u"Failed to extract data with regex! => {}\n".format(field)
|
err_msg = "Failed to extract data with regex! => {}\n".format(field)
|
||||||
err_msg += u"response body: {}\n".format(self.text)
|
err_msg += "response body: {}\n".format(self.text)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ExtractFailure(err_msg)
|
raise exceptions.ExtractFailure(err_msg)
|
||||||
|
|
||||||
return matched.group(1)
|
return matched.group(1)
|
||||||
|
|
||||||
def _extract_field_with_delimiter(self, field):
|
def _extract_field_with_delimiter(self, field):
|
||||||
""" response content could be json or html text.
|
"""response content could be json or html text.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
field (str): string joined by delimiter.
|
field (str): string joined by delimiter.
|
||||||
@@ -111,7 +110,7 @@ class ResponseObject(object):
|
|||||||
# string.split(sep=None, maxsplit=1) -> list of strings
|
# string.split(sep=None, maxsplit=1) -> list of strings
|
||||||
# e.g. "content.person.name" => ["content", "person.name"]
|
# e.g. "content.person.name" => ["content", "person.name"]
|
||||||
try:
|
try:
|
||||||
top_query, sub_query = field.split('.', 1)
|
top_query, sub_query = field.split(".", 1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
top_query = field
|
top_query = field
|
||||||
sub_query = None
|
sub_query = None
|
||||||
@@ -120,7 +119,7 @@ class ResponseObject(object):
|
|||||||
if top_query in ["status_code", "encoding", "ok", "reason", "url"]:
|
if top_query in ["status_code", "encoding", "ok", "reason", "url"]:
|
||||||
if sub_query:
|
if sub_query:
|
||||||
# status_code.XX
|
# status_code.XX
|
||||||
err_msg = u"Failed to extract: {}\n".format(field)
|
err_msg = "Failed to extract: {}\n".format(field)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ParamsError(err_msg)
|
raise exceptions.ParamsError(err_msg)
|
||||||
|
|
||||||
@@ -136,16 +135,18 @@ class ResponseObject(object):
|
|||||||
try:
|
try:
|
||||||
return cookies[sub_query]
|
return cookies[sub_query]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
err_msg = u"Failed to extract cookie! => {}\n".format(field)
|
err_msg = "Failed to extract cookie! => {}\n".format(field)
|
||||||
err_msg += u"response cookies: {}\n".format(cookies)
|
err_msg += "response cookies: {}\n".format(cookies)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ExtractFailure(err_msg)
|
raise exceptions.ExtractFailure(err_msg)
|
||||||
|
|
||||||
# elapsed
|
# elapsed
|
||||||
elif top_query == "elapsed":
|
elif top_query == "elapsed":
|
||||||
available_attributes = u"available attributes: days, seconds, microseconds, total_seconds"
|
available_attributes = (
|
||||||
|
"available attributes: days, seconds, microseconds, total_seconds"
|
||||||
|
)
|
||||||
if not sub_query:
|
if not sub_query:
|
||||||
err_msg = u"elapsed is datetime.timedelta instance, attribute should also be specified!\n"
|
err_msg = "elapsed is datetime.timedelta instance, attribute should also be specified!\n"
|
||||||
err_msg += available_attributes
|
err_msg += available_attributes
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ParamsError(err_msg)
|
raise exceptions.ParamsError(err_msg)
|
||||||
@@ -154,7 +155,9 @@ class ResponseObject(object):
|
|||||||
elif sub_query == "total_seconds":
|
elif sub_query == "total_seconds":
|
||||||
return self.elapsed.total_seconds()
|
return self.elapsed.total_seconds()
|
||||||
else:
|
else:
|
||||||
err_msg = "{} is not valid datetime.timedelta attribute.\n".format(sub_query)
|
err_msg = "{} is not valid datetime.timedelta attribute.\n".format(
|
||||||
|
sub_query
|
||||||
|
)
|
||||||
err_msg += available_attributes
|
err_msg += available_attributes
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ParamsError(err_msg)
|
raise exceptions.ParamsError(err_msg)
|
||||||
@@ -169,8 +172,8 @@ class ResponseObject(object):
|
|||||||
try:
|
try:
|
||||||
return headers[sub_query]
|
return headers[sub_query]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
err_msg = u"Failed to extract header! => {}\n".format(field)
|
err_msg = "Failed to extract header! => {}\n".format(field)
|
||||||
err_msg += u"response headers: {}\n".format(headers)
|
err_msg += "response headers: {}\n".format(headers)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ExtractFailure(err_msg)
|
raise exceptions.ExtractFailure(err_msg)
|
||||||
|
|
||||||
@@ -193,8 +196,12 @@ class ResponseObject(object):
|
|||||||
return utils.query_json(body, sub_query)
|
return utils.query_json(body, sub_query)
|
||||||
else:
|
else:
|
||||||
# content = "<html>abcdefg</html>", content.xxx
|
# content = "<html>abcdefg</html>", content.xxx
|
||||||
err_msg = u"Failed to extract attribute from response body! => {}\n".format(field)
|
err_msg = (
|
||||||
err_msg += u"response body: {}\n".format(body)
|
"Failed to extract attribute from response body! => {}\n".format(
|
||||||
|
field
|
||||||
|
)
|
||||||
|
)
|
||||||
|
err_msg += "response body: {}\n".format(body)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ExtractFailure(err_msg)
|
raise exceptions.ExtractFailure(err_msg)
|
||||||
|
|
||||||
@@ -214,26 +221,29 @@ class ResponseObject(object):
|
|||||||
return utils.query_json(attributes, sub_query)
|
return utils.query_json(attributes, sub_query)
|
||||||
else:
|
else:
|
||||||
# content = "attributes.new_attribute_not_exist"
|
# content = "attributes.new_attribute_not_exist"
|
||||||
err_msg = u"Failed to extract cumstom set attribute from teardown hooks! => {}\n".format(field)
|
err_msg = "Failed to extract cumstom set attribute from teardown hooks! => {}\n".format(
|
||||||
err_msg += u"response set attributes: {}\n".format(attributes)
|
field
|
||||||
|
)
|
||||||
|
err_msg += "response set attributes: {}\n".format(attributes)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.TeardownHooksFailure(err_msg)
|
raise exceptions.TeardownHooksFailure(err_msg)
|
||||||
|
|
||||||
# others
|
# others
|
||||||
else:
|
else:
|
||||||
err_msg = u"Failed to extract attribute from response! => {}\n".format(field)
|
err_msg = "Failed to extract attribute from response! => {}\n".format(field)
|
||||||
err_msg += u"available response attributes: status_code, cookies, elapsed, headers, content, " \
|
err_msg += (
|
||||||
u"text, json, encoding, ok, reason, url.\n\n"
|
"available response attributes: status_code, cookies, elapsed, headers, content, "
|
||||||
err_msg += u"If you want to set attribute in teardown_hooks, take the following example as reference:\n"
|
"text, json, encoding, ok, reason, url.\n\n"
|
||||||
err_msg += u"response.new_attribute = 'new_attribute_value'\n"
|
)
|
||||||
|
err_msg += "If you want to set attribute in teardown_hooks, take the following example as reference:\n"
|
||||||
|
err_msg += "response.new_attribute = 'new_attribute_value'\n"
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ParamsError(err_msg)
|
raise exceptions.ParamsError(err_msg)
|
||||||
|
|
||||||
def extract_field(self, field):
|
def extract_field(self, field):
|
||||||
""" extract value from requests.Response.
|
"""extract value from requests.Response."""
|
||||||
"""
|
|
||||||
if not isinstance(field, basestring):
|
if not isinstance(field, basestring):
|
||||||
err_msg = u"Invalid extractor! => {}\n".format(field)
|
err_msg = "Invalid extractor! => {}\n".format(field)
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ParamsError(err_msg)
|
raise exceptions.ParamsError(err_msg)
|
||||||
|
|
||||||
@@ -255,7 +265,7 @@ class ResponseObject(object):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
def extract_response(self, extractors):
|
def extract_response(self, extractors):
|
||||||
""" extract value from requests.Response and store in OrderedDict.
|
"""extract value from requests.Response and store in OrderedDict.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
extractors (list):
|
extractors (list):
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class HookTypeEnum(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class Runner(object):
|
class Runner(object):
|
||||||
""" Running testcases.
|
"""Running testcases.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
>>> tests_mapping = {
|
>>> tests_mapping = {
|
||||||
@@ -52,7 +52,7 @@ class Runner(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config, http_client_session=None):
|
def __init__(self, config, http_client_session=None):
|
||||||
""" run testcase or testsuite.
|
"""run testcase or testsuite.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (dict): testcase/testsuite config dict
|
config (dict): testcase/testsuite config dict
|
||||||
@@ -87,15 +87,14 @@ class Runner(object):
|
|||||||
self.do_hook_actions(self.testcase_teardown_hooks, HookTypeEnum.TEARDOWN)
|
self.do_hook_actions(self.testcase_teardown_hooks, HookTypeEnum.TEARDOWN)
|
||||||
|
|
||||||
def __clear_test_data(self):
|
def __clear_test_data(self):
|
||||||
""" clear request and response data
|
"""clear request and response data"""
|
||||||
"""
|
|
||||||
if not isinstance(self.http_client_session, HttpSession):
|
if not isinstance(self.http_client_session, HttpSession):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.http_client_session.init_meta_data()
|
self.http_client_session.init_meta_data()
|
||||||
|
|
||||||
def _handle_skip_feature(self, test_dict):
|
def _handle_skip_feature(self, test_dict):
|
||||||
""" handle skip feature for test
|
"""handle skip feature for test
|
||||||
- skip: skip current test unconditionally
|
- skip: skip current test unconditionally
|
||||||
- skipIf: skip current test if condition is true
|
- skipIf: skip current test if condition is true
|
||||||
- skipUnless: skip current test unless condition is true
|
- skipUnless: skip current test unless condition is true
|
||||||
@@ -127,7 +126,7 @@ class Runner(object):
|
|||||||
raise SkipTest(skip_reason)
|
raise SkipTest(skip_reason)
|
||||||
|
|
||||||
def do_hook_actions(self, actions, hook_type):
|
def do_hook_actions(self, actions, hook_type):
|
||||||
""" call hook actions.
|
"""call hook actions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
actions (list): each action in actions list maybe in two format.
|
actions (list): each action in actions list maybe in two format.
|
||||||
@@ -153,9 +152,7 @@ class Runner(object):
|
|||||||
var_name, hook_content, hook_content_eval
|
var_name, hook_content, hook_content_eval
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.session_context.update_test_variables(
|
self.session_context.update_test_variables(var_name, hook_content_eval)
|
||||||
var_name, hook_content_eval
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# format 2
|
# format 2
|
||||||
logger.log_debug("call hook function: {}".format(action))
|
logger.log_debug("call hook function: {}".format(action))
|
||||||
@@ -163,7 +160,7 @@ class Runner(object):
|
|||||||
self.session_context.eval_content(action)
|
self.session_context.eval_content(action)
|
||||||
|
|
||||||
def _run_test(self, test_dict):
|
def _run_test(self, test_dict):
|
||||||
""" run single teststep.
|
"""run single teststep.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
test_dict (dict): teststep info
|
test_dict (dict): teststep info
|
||||||
@@ -209,7 +206,7 @@ class Runner(object):
|
|||||||
test_name = self.session_context.eval_content(test_dict.get("name", ""))
|
test_name = self.session_context.eval_content(test_dict.get("name", ""))
|
||||||
|
|
||||||
# parse test request
|
# parse test request
|
||||||
raw_request = test_dict.get('request', {})
|
raw_request = test_dict.get("request", {})
|
||||||
parsed_test_request = self.session_context.eval_content(raw_request)
|
parsed_test_request = self.session_context.eval_content(raw_request)
|
||||||
self.session_context.update_test_variables("request", parsed_test_request)
|
self.session_context.update_test_variables("request", parsed_test_request)
|
||||||
|
|
||||||
@@ -219,12 +216,12 @@ class Runner(object):
|
|||||||
self.do_hook_actions(setup_hooks, HookTypeEnum.SETUP)
|
self.do_hook_actions(setup_hooks, HookTypeEnum.SETUP)
|
||||||
|
|
||||||
# prepend url with base_url unless it's already an absolute URL
|
# prepend url with base_url unless it's already an absolute URL
|
||||||
url = parsed_test_request.pop('url')
|
url = parsed_test_request.pop("url")
|
||||||
base_url = self.session_context.eval_content(test_dict.get("base_url", ""))
|
base_url = self.session_context.eval_content(test_dict.get("base_url", ""))
|
||||||
parsed_url = utils.build_url(base_url, url)
|
parsed_url = utils.build_url(base_url, url)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
method = parsed_test_request.pop('method')
|
method = parsed_test_request.pop("method")
|
||||||
parsed_test_request.setdefault("verify", self.verify)
|
parsed_test_request.setdefault("verify", self.verify)
|
||||||
group_name = parsed_test_request.pop("group", None)
|
group_name = parsed_test_request.pop("group", None)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -232,14 +229,12 @@ class Runner(object):
|
|||||||
|
|
||||||
logger.log_info("{method} {url}".format(method=method, url=parsed_url))
|
logger.log_info("{method} {url}".format(method=method, url=parsed_url))
|
||||||
logger.log_debug(
|
logger.log_debug(
|
||||||
"request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request))
|
"request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request)
|
||||||
|
)
|
||||||
|
|
||||||
# request
|
# request
|
||||||
resp = self.http_client_session.request(
|
resp = self.http_client_session.request(
|
||||||
method,
|
method, parsed_url, name=(group_name or test_name), **parsed_test_request
|
||||||
parsed_url,
|
|
||||||
name=(group_name or test_name),
|
|
||||||
**parsed_test_request
|
|
||||||
)
|
)
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
@@ -284,10 +279,7 @@ class Runner(object):
|
|||||||
validators = test_dict.get("validate") or test_dict.get("validators") or []
|
validators = test_dict.get("validate") or test_dict.get("validators") or []
|
||||||
validate_script = test_dict.get("validate_script", [])
|
validate_script = test_dict.get("validate_script", [])
|
||||||
if validate_script:
|
if validate_script:
|
||||||
validators.append({
|
validators.append({"type": "python_script", "script": validate_script})
|
||||||
"type": "python_script",
|
|
||||||
"script": validate_script
|
|
||||||
})
|
|
||||||
|
|
||||||
validator = Validator(self.session_context, resp_obj)
|
validator = Validator(self.session_context, resp_obj)
|
||||||
try:
|
try:
|
||||||
@@ -299,8 +291,7 @@ class Runner(object):
|
|||||||
self.validation_results = validator.validation_results
|
self.validation_results = validator.validation_results
|
||||||
|
|
||||||
def _run_testcase(self, testcase_dict):
|
def _run_testcase(self, testcase_dict):
|
||||||
""" run single testcase.
|
"""run single testcase."""
|
||||||
"""
|
|
||||||
self.meta_datas = []
|
self.meta_datas = []
|
||||||
config = testcase_dict.get("config", {})
|
config = testcase_dict.get("config", {})
|
||||||
|
|
||||||
@@ -333,7 +324,7 @@ class Runner(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def run_test(self, test_dict):
|
def run_test(self, test_dict):
|
||||||
""" run single teststep of testcase.
|
"""run single teststep of testcase.
|
||||||
test_dict may be in 3 types.
|
test_dict may be in 3 types.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -370,7 +361,8 @@ class Runner(object):
|
|||||||
# nested testcase
|
# nested testcase
|
||||||
test_dict.setdefault("config", {}).setdefault("variables", {})
|
test_dict.setdefault("config", {}).setdefault("variables", {})
|
||||||
test_dict["config"]["variables"].update(
|
test_dict["config"]["variables"].update(
|
||||||
self.session_context.session_variables_mapping)
|
self.session_context.session_variables_mapping
|
||||||
|
)
|
||||||
self._run_testcase(test_dict)
|
self._run_testcase(test_dict)
|
||||||
else:
|
else:
|
||||||
# api
|
# api
|
||||||
@@ -388,8 +380,7 @@ class Runner(object):
|
|||||||
self.meta_datas["validators"] = self.validation_results
|
self.meta_datas["validators"] = self.validation_results
|
||||||
|
|
||||||
def export_variables(self, output_variables_list):
|
def export_variables(self, output_variables_list):
|
||||||
""" export current testcase variables
|
"""export current testcase variables"""
|
||||||
"""
|
|
||||||
variables_mapping = self.session_context.session_variables_mapping
|
variables_mapping = self.session_context.session_variables_mapping
|
||||||
|
|
||||||
output = {}
|
output = {}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ absolute_http_url_regexp = re.compile(r"^https?://", re.I)
|
|||||||
def init_sentry_sdk():
|
def init_sentry_sdk():
|
||||||
sentry_sdk.init(
|
sentry_sdk.init(
|
||||||
dsn="https://cc6dd86fbe9f4e7fbd95248cfcff114d@sentry.io/1862849",
|
dsn="https://cc6dd86fbe9f4e7fbd95248cfcff114d@sentry.io/1862849",
|
||||||
release="httprunner@{}".format(__version__)
|
release="httprunner@{}".format(__version__),
|
||||||
)
|
)
|
||||||
|
|
||||||
with sentry_sdk.configure_scope() as scope:
|
with sentry_sdk.configure_scope() as scope:
|
||||||
@@ -32,46 +32,48 @@ def init_sentry_sdk():
|
|||||||
|
|
||||||
class GAClient(object):
|
class GAClient(object):
|
||||||
|
|
||||||
version = '1' # GA API Version
|
version = "1" # GA API Version
|
||||||
report_url = 'https://www.google-analytics.com/collect'
|
report_url = "https://www.google-analytics.com/collect"
|
||||||
report_debug_url = 'https://www.google-analytics.com/debug/collect' # used for debug
|
report_debug_url = (
|
||||||
|
"https://www.google-analytics.com/debug/collect" # used for debug
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, tracking_id):
|
def __init__(self, tracking_id):
|
||||||
self.http_client = requests.Session()
|
self.http_client = requests.Session()
|
||||||
self.label = str(__version__)
|
self.label = str(__version__)
|
||||||
self.common_params = {
|
self.common_params = {
|
||||||
'v': self.version,
|
"v": self.version,
|
||||||
'tid': tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X
|
"tid": tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X
|
||||||
'cid': uuid.getnode(), # Anonymous Client ID
|
"cid": uuid.getnode(), # Anonymous Client ID
|
||||||
'ua': 'HttpRunner/{}'.format(__version__),
|
"ua": "HttpRunner/{}".format(__version__),
|
||||||
}
|
}
|
||||||
|
|
||||||
def track_event(self, category, action, value=0):
|
def track_event(self, category, action, value=0):
|
||||||
data = {
|
data = {
|
||||||
't': 'event', # Event hit type = event
|
"t": "event", # Event hit type = event
|
||||||
'ec': category, # Required. Event Category.
|
"ec": category, # Required. Event Category.
|
||||||
'ea': action, # Required. Event Action.
|
"ea": action, # Required. Event Action.
|
||||||
'el': self.label, # Optional. Event label, used as version.
|
"el": self.label, # Optional. Event label, used as version.
|
||||||
'ev': value, # Optional. Event value, must be non-negative integer
|
"ev": value, # Optional. Event value, must be non-negative integer
|
||||||
}
|
}
|
||||||
data.update(self.common_params)
|
data.update(self.common_params)
|
||||||
try:
|
try:
|
||||||
self.http_client.post(self.report_url, data=data, timeout=5)
|
self.http_client.post(self.report_url, data=data, timeout=5)
|
||||||
except Exception: # ProxyError, SSLError, ConnectionError
|
except Exception: # ProxyError, SSLError, ConnectionError
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def track_user_timing(self, category, variable, duration):
|
def track_user_timing(self, category, variable, duration):
|
||||||
data = {
|
data = {
|
||||||
't': 'timing', # Event hit type = timing
|
"t": "timing", # Event hit type = timing
|
||||||
'utc': category, # Required. user timing category. e.g. jsonLoader
|
"utc": category, # Required. user timing category. e.g. jsonLoader
|
||||||
'utv': variable, # Required. timing variable. e.g. load
|
"utv": variable, # Required. timing variable. e.g. load
|
||||||
'utt': duration, # Required. time took duration.
|
"utt": duration, # Required. time took duration.
|
||||||
'utl': self.label, # Optional. user timing label, used as version.
|
"utl": self.label, # Optional. user timing label, used as version.
|
||||||
}
|
}
|
||||||
data.update(self.common_params)
|
data.update(self.common_params)
|
||||||
try:
|
try:
|
||||||
self.http_client.post(self.report_url, data=data, timeout=5)
|
self.http_client.post(self.report_url, data=data, timeout=5)
|
||||||
except Exception: # ProxyError, SSLError, ConnectionError
|
except Exception: # ProxyError, SSLError, ConnectionError
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -79,23 +81,21 @@ ga_client = GAClient("UA-114587036-1")
|
|||||||
|
|
||||||
|
|
||||||
def set_os_environ(variables_mapping):
|
def set_os_environ(variables_mapping):
|
||||||
""" set variables mapping to os.environ
|
"""set variables mapping to os.environ"""
|
||||||
"""
|
|
||||||
for variable in variables_mapping:
|
for variable in variables_mapping:
|
||||||
os.environ[variable] = variables_mapping[variable]
|
os.environ[variable] = variables_mapping[variable]
|
||||||
logger.log_debug("Set OS environment variable: {}".format(variable))
|
logger.log_debug("Set OS environment variable: {}".format(variable))
|
||||||
|
|
||||||
|
|
||||||
def unset_os_environ(variables_mapping):
|
def unset_os_environ(variables_mapping):
|
||||||
""" set variables mapping to os.environ
|
"""set variables mapping to os.environ"""
|
||||||
"""
|
|
||||||
for variable in variables_mapping:
|
for variable in variables_mapping:
|
||||||
os.environ.pop(variable)
|
os.environ.pop(variable)
|
||||||
logger.log_debug("Unset OS environment variable: {}".format(variable))
|
logger.log_debug("Unset OS environment variable: {}".format(variable))
|
||||||
|
|
||||||
|
|
||||||
def get_os_environ(variable_name):
|
def get_os_environ(variable_name):
|
||||||
""" get value of environment variable.
|
"""get value of environment variable.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
variable_name(str): variable name
|
variable_name(str): variable name
|
||||||
@@ -114,7 +114,7 @@ def get_os_environ(variable_name):
|
|||||||
|
|
||||||
|
|
||||||
def build_url(base_url, path):
|
def build_url(base_url, path):
|
||||||
""" prepend url with base_url unless it's already an absolute URL """
|
"""prepend url with base_url unless it's already an absolute URL"""
|
||||||
if absolute_http_url_regexp.match(path):
|
if absolute_http_url_regexp.match(path):
|
||||||
return path
|
return path
|
||||||
elif base_url:
|
elif base_url:
|
||||||
@@ -123,8 +123,8 @@ def build_url(base_url, path):
|
|||||||
raise ParamsError("base url missed!")
|
raise ParamsError("base url missed!")
|
||||||
|
|
||||||
|
|
||||||
def query_json(json_content, query, delimiter='.'):
|
def query_json(json_content, query, delimiter="."):
|
||||||
""" Do an xpath-like query with json_content.
|
"""Do an xpath-like query with json_content.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_content (dict/list/string): content to be queried.
|
json_content (dict/list/string): content to be queried.
|
||||||
@@ -158,7 +158,7 @@ def query_json(json_content, query, delimiter='.'):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
raise_flag = False
|
raise_flag = False
|
||||||
response_body = u"response body: {}\n".format(json_content)
|
response_body = "response body: {}\n".format(json_content)
|
||||||
try:
|
try:
|
||||||
for key in query.split(delimiter):
|
for key in query.split(delimiter):
|
||||||
if isinstance(json_content, (list, basestring)):
|
if isinstance(json_content, (list, basestring)):
|
||||||
@@ -167,13 +167,16 @@ def query_json(json_content, query, delimiter='.'):
|
|||||||
json_content = json_content[key]
|
json_content = json_content[key]
|
||||||
else:
|
else:
|
||||||
logger.log_error(
|
logger.log_error(
|
||||||
"invalid type value: {}({})".format(json_content, type(json_content)))
|
"invalid type value: {}({})".format(
|
||||||
|
json_content, type(json_content)
|
||||||
|
)
|
||||||
|
)
|
||||||
raise_flag = True
|
raise_flag = True
|
||||||
except (KeyError, ValueError, IndexError):
|
except (KeyError, ValueError, IndexError):
|
||||||
raise_flag = True
|
raise_flag = True
|
||||||
|
|
||||||
if raise_flag:
|
if raise_flag:
|
||||||
err_msg = u"Failed to extract! => {}\n".format(query)
|
err_msg = "Failed to extract! => {}\n".format(query)
|
||||||
err_msg += response_body
|
err_msg += response_body
|
||||||
logger.log_error(err_msg)
|
logger.log_error(err_msg)
|
||||||
raise exceptions.ExtractFailure(err_msg)
|
raise exceptions.ExtractFailure(err_msg)
|
||||||
@@ -182,7 +185,7 @@ def query_json(json_content, query, delimiter='.'):
|
|||||||
|
|
||||||
|
|
||||||
def lower_dict_keys(origin_dict):
|
def lower_dict_keys(origin_dict):
|
||||||
""" convert keys in dict to lower case
|
"""convert keys in dict to lower case
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
origin_dict (dict): mapping data structure
|
origin_dict (dict): mapping data structure
|
||||||
@@ -213,16 +216,13 @@ def lower_dict_keys(origin_dict):
|
|||||||
if not origin_dict or not isinstance(origin_dict, dict):
|
if not origin_dict or not isinstance(origin_dict, dict):
|
||||||
return origin_dict
|
return origin_dict
|
||||||
|
|
||||||
return {
|
return {key.lower(): value for key, value in origin_dict.items()}
|
||||||
key.lower(): value
|
|
||||||
for key, value in origin_dict.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def lower_test_dict_keys(test_dict):
|
def lower_test_dict_keys(test_dict):
|
||||||
""" convert keys in test_dict to lower case, convertion will occur in two places:
|
"""convert keys in test_dict to lower case, convertion will occur in two places:
|
||||||
1, all keys in test_dict;
|
1, all keys in test_dict;
|
||||||
2, all keys in test_dict["request"]
|
2, all keys in test_dict["request"]
|
||||||
"""
|
"""
|
||||||
# convert keys in test_dict
|
# convert keys in test_dict
|
||||||
test_dict = lower_dict_keys(test_dict)
|
test_dict = lower_dict_keys(test_dict)
|
||||||
@@ -235,7 +235,7 @@ def lower_test_dict_keys(test_dict):
|
|||||||
|
|
||||||
|
|
||||||
def deepcopy_dict(data):
|
def deepcopy_dict(data):
|
||||||
""" deepcopy dict data, ignore file object (_io.BufferedReader)
|
"""deepcopy dict data, ignore file object (_io.BufferedReader)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (dict): dict data structure
|
data (dict): dict data structure
|
||||||
@@ -271,7 +271,7 @@ def deepcopy_dict(data):
|
|||||||
|
|
||||||
|
|
||||||
def ensure_mapping_format(variables):
|
def ensure_mapping_format(variables):
|
||||||
""" ensure variables are in mapping format.
|
"""ensure variables are in mapping format.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
variables (list/dict): original variables
|
variables (list/dict): original variables
|
||||||
@@ -306,7 +306,7 @@ def ensure_mapping_format(variables):
|
|||||||
|
|
||||||
|
|
||||||
def extend_variables(raw_variables, override_variables):
|
def extend_variables(raw_variables, override_variables):
|
||||||
""" extend raw_variables with override_variables.
|
"""extend raw_variables with override_variables.
|
||||||
override_variables will merge and override raw_variables.
|
override_variables will merge and override raw_variables.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -343,7 +343,7 @@ def extend_variables(raw_variables, override_variables):
|
|||||||
|
|
||||||
|
|
||||||
def get_testcase_io(testcase):
|
def get_testcase_io(testcase):
|
||||||
""" get and print testcase input(variables) and output(export).
|
"""get and print testcase input(variables) and output(export).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
testcase (unittest.suite.TestSuite): corresponding to one YAML/JSON file, it has been set two attributes:
|
testcase (unittest.suite.TestSuite): corresponding to one YAML/JSON file, it has been set two attributes:
|
||||||
@@ -355,18 +355,14 @@ def get_testcase_io(testcase):
|
|||||||
"""
|
"""
|
||||||
test_runner = testcase.runner
|
test_runner = testcase.runner
|
||||||
variables = testcase.config.get("variables", {})
|
variables = testcase.config.get("variables", {})
|
||||||
output_list = testcase.config.get("export") \
|
output_list = testcase.config.get("export") or testcase.config.get("output", [])
|
||||||
or testcase.config.get("output", [])
|
|
||||||
export_mapping = test_runner.export_variables(output_list)
|
export_mapping = test_runner.export_variables(output_list)
|
||||||
|
|
||||||
return {
|
return {"in": variables, "out": export_mapping}
|
||||||
"in": variables,
|
|
||||||
"out": export_mapping
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def print_info(info_mapping):
|
def print_info(info_mapping):
|
||||||
""" print info in mapping.
|
"""print info in mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
info_mapping (dict): input(variables) or output mapping.
|
info_mapping (dict): input(variables) or output mapping.
|
||||||
@@ -417,10 +413,11 @@ def print_info(info_mapping):
|
|||||||
|
|
||||||
|
|
||||||
def create_scaffold(project_name):
|
def create_scaffold(project_name):
|
||||||
""" create scaffold with specified project name.
|
"""create scaffold with specified project name."""
|
||||||
"""
|
|
||||||
if os.path.isdir(project_name):
|
if os.path.isdir(project_name):
|
||||||
logger.log_warning(u"Folder {} exists, please specify a new folder name.".format(project_name))
|
logger.log_warning(
|
||||||
|
"Folder {} exists, please specify a new folder name.".format(project_name)
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
ga_client.track_event("Scaffold", "startproject")
|
ga_client.track_event("Scaffold", "startproject")
|
||||||
@@ -433,7 +430,7 @@ def create_scaffold(project_name):
|
|||||||
logger.color_print(msg, "BLUE")
|
logger.color_print(msg, "BLUE")
|
||||||
|
|
||||||
def create_file(path, file_content=""):
|
def create_file(path, file_content=""):
|
||||||
with open(path, 'w') as f:
|
with open(path, "w") as f:
|
||||||
f.write(file_content)
|
f.write(file_content)
|
||||||
msg = "created file: {}".format(path)
|
msg = "created file: {}".format(path)
|
||||||
logger.color_print(msg, "BLUE")
|
logger.color_print(msg, "BLUE")
|
||||||
@@ -498,24 +495,16 @@ testcases:
|
|||||||
variables:
|
variables:
|
||||||
device_sn: $device_sn
|
device_sn: $device_sn
|
||||||
"""
|
"""
|
||||||
ignore_content = "\n".join([
|
ignore_content = "\n".join(
|
||||||
".env",
|
[".env", "reports/*", "__pycache__/*", "*.pyc", ".python-version", "logs/*"]
|
||||||
"reports/*",
|
)
|
||||||
"__pycache__/*",
|
|
||||||
"*.pyc",
|
|
||||||
".python-version",
|
|
||||||
"logs/*"
|
|
||||||
])
|
|
||||||
demo_debugtalk_content = """
|
demo_debugtalk_content = """
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def sleep(n_secs):
|
def sleep(n_secs):
|
||||||
time.sleep(n_secs)
|
time.sleep(n_secs)
|
||||||
"""
|
"""
|
||||||
demo_env_content = "\n".join([
|
demo_env_content = "\n".join(["USERNAME=leolee", "PASSWORD=123456"])
|
||||||
"USERNAME=leolee",
|
|
||||||
"PASSWORD=123456"
|
|
||||||
])
|
|
||||||
|
|
||||||
create_folder(project_name)
|
create_folder(project_name)
|
||||||
create_folder(os.path.join(project_name, "api"))
|
create_folder(os.path.join(project_name, "api"))
|
||||||
@@ -523,15 +512,21 @@ def sleep(n_secs):
|
|||||||
create_folder(os.path.join(project_name, "testsuites"))
|
create_folder(os.path.join(project_name, "testsuites"))
|
||||||
create_folder(os.path.join(project_name, "reports"))
|
create_folder(os.path.join(project_name, "reports"))
|
||||||
create_file(os.path.join(project_name, "api", "demo_api.yml"), demo_api_content)
|
create_file(os.path.join(project_name, "api", "demo_api.yml"), demo_api_content)
|
||||||
create_file(os.path.join(project_name, "testcases", "demo_testcase.yml"), demo_testcase_content)
|
create_file(
|
||||||
create_file(os.path.join(project_name, "testsuites", "demo_testsuite.yml"), demo_testsuite_content)
|
os.path.join(project_name, "testcases", "demo_testcase.yml"),
|
||||||
|
demo_testcase_content,
|
||||||
|
)
|
||||||
|
create_file(
|
||||||
|
os.path.join(project_name, "testsuites", "demo_testsuite.yml"),
|
||||||
|
demo_testsuite_content,
|
||||||
|
)
|
||||||
create_file(os.path.join(project_name, "debugtalk.py"), demo_debugtalk_content)
|
create_file(os.path.join(project_name, "debugtalk.py"), demo_debugtalk_content)
|
||||||
create_file(os.path.join(project_name, ".env"), demo_env_content)
|
create_file(os.path.join(project_name, ".env"), demo_env_content)
|
||||||
create_file(os.path.join(project_name, ".gitignore"), ignore_content)
|
create_file(os.path.join(project_name, ".gitignore"), ignore_content)
|
||||||
|
|
||||||
|
|
||||||
def gen_cartesian_product(*args):
|
def gen_cartesian_product(*args):
|
||||||
""" generate cartesian product for lists
|
"""generate cartesian product for lists
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
args (list of list): lists to be generated with cartesian product
|
args (list of list): lists to be generated with cartesian product
|
||||||
@@ -572,11 +567,12 @@ def gen_cartesian_product(*args):
|
|||||||
|
|
||||||
|
|
||||||
def prettify_json_file(file_list):
|
def prettify_json_file(file_list):
|
||||||
""" prettify JSON testcase format
|
"""prettify JSON testcase format"""
|
||||||
"""
|
|
||||||
for json_file in set(file_list):
|
for json_file in set(file_list):
|
||||||
if not json_file.endswith(".json"):
|
if not json_file.endswith(".json"):
|
||||||
logger.log_warning("Only JSON file format can be prettified, skip: {}".format(json_file))
|
logger.log_warning(
|
||||||
|
"Only JSON file format can be prettified, skip: {}".format(json_file)
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.color_print("Start to prettify JSON file: {}".format(json_file), "GREEN")
|
logger.color_print("Start to prettify JSON file: {}".format(json_file), "GREEN")
|
||||||
@@ -585,22 +581,21 @@ def prettify_json_file(file_list):
|
|||||||
file_name, file_suffix = os.path.splitext(os.path.basename(json_file))
|
file_name, file_suffix = os.path.splitext(os.path.basename(json_file))
|
||||||
outfile = os.path.join(dir_path, "{}.pretty.json".format(file_name))
|
outfile = os.path.join(dir_path, "{}.pretty.json".format(file_name))
|
||||||
|
|
||||||
with io.open(json_file, 'r', encoding='utf-8') as stream:
|
with io.open(json_file, "r", encoding="utf-8") as stream:
|
||||||
try:
|
try:
|
||||||
obj = json.load(stream)
|
obj = json.load(stream)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise SystemExit(e)
|
raise SystemExit(e)
|
||||||
|
|
||||||
with io.open(outfile, 'w', encoding='utf-8') as out:
|
with io.open(outfile, "w", encoding="utf-8") as out:
|
||||||
json.dump(obj, out, indent=4, separators=(',', ': '))
|
json.dump(obj, out, indent=4, separators=(",", ": "))
|
||||||
out.write('\n')
|
out.write("\n")
|
||||||
|
|
||||||
print("success: {}".format(outfile))
|
print("success: {}".format(outfile))
|
||||||
|
|
||||||
|
|
||||||
def omit_long_data(body, omit_len=512):
|
def omit_long_data(body, omit_len=512):
|
||||||
""" omit too long str/bytes
|
"""omit too long str/bytes"""
|
||||||
"""
|
|
||||||
if not isinstance(body, basestring):
|
if not isinstance(body, basestring):
|
||||||
return body
|
return body
|
||||||
|
|
||||||
@@ -618,8 +613,8 @@ def omit_long_data(body, omit_len=512):
|
|||||||
|
|
||||||
|
|
||||||
def dump_json_file(json_data, json_file_abs_path):
|
def dump_json_file(json_data, json_file_abs_path):
|
||||||
""" dump json data to file
|
"""dump json data to file"""
|
||||||
"""
|
|
||||||
class PythonObjectEncoder(json.JSONEncoder):
|
class PythonObjectEncoder(json.JSONEncoder):
|
||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
try:
|
try:
|
||||||
@@ -632,26 +627,28 @@ def dump_json_file(json_data, json_file_abs_path):
|
|||||||
os.makedirs(file_foder_path)
|
os.makedirs(file_foder_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with io.open(json_file_abs_path, 'w', encoding='utf-8') as outfile:
|
with io.open(json_file_abs_path, "w", encoding="utf-8") as outfile:
|
||||||
if is_py2:
|
if is_py2:
|
||||||
outfile.write(
|
outfile.write(
|
||||||
unicode(json.dumps(
|
unicode(
|
||||||
json_data,
|
json.dumps(
|
||||||
indent=4,
|
json_data,
|
||||||
separators=(',', ':'),
|
indent=4,
|
||||||
encoding="utf8",
|
separators=(",", ":"),
|
||||||
ensure_ascii=False,
|
encoding="utf8",
|
||||||
cls=PythonObjectEncoder
|
ensure_ascii=False,
|
||||||
))
|
cls=PythonObjectEncoder,
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
json.dump(
|
json.dump(
|
||||||
json_data,
|
json_data,
|
||||||
outfile,
|
outfile,
|
||||||
indent=4,
|
indent=4,
|
||||||
separators=(',', ':'),
|
separators=(",", ":"),
|
||||||
ensure_ascii=False,
|
ensure_ascii=False,
|
||||||
cls=PythonObjectEncoder
|
cls=PythonObjectEncoder,
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = "dump file: {}".format(json_file_abs_path)
|
msg = "dump file: {}".format(json_file_abs_path)
|
||||||
@@ -663,8 +660,7 @@ def dump_json_file(json_data, json_file_abs_path):
|
|||||||
|
|
||||||
|
|
||||||
def prepare_dump_json_file_abs_path(project_mapping, tag_name):
|
def prepare_dump_json_file_abs_path(project_mapping, tag_name):
|
||||||
""" prepare dump json file absolute path.
|
"""prepare dump json file absolute path."""
|
||||||
"""
|
|
||||||
pwd_dir_path = project_mapping.get("PWD") or os.getcwd()
|
pwd_dir_path = project_mapping.get("PWD") or os.getcwd()
|
||||||
test_path = project_mapping.get("test_path")
|
test_path = project_mapping.get("test_path")
|
||||||
|
|
||||||
@@ -676,7 +672,7 @@ def prepare_dump_json_file_abs_path(project_mapping, tag_name):
|
|||||||
|
|
||||||
# both test_path and pwd_dir_path are absolute path
|
# both test_path and pwd_dir_path are absolute path
|
||||||
logs_dir_path = os.path.join(pwd_dir_path, "logs")
|
logs_dir_path = os.path.join(pwd_dir_path, "logs")
|
||||||
test_path_relative_path = test_path[len(pwd_dir_path)+1:]
|
test_path_relative_path = test_path[len(pwd_dir_path) + 1 :]
|
||||||
|
|
||||||
if os.path.isdir(test_path):
|
if os.path.isdir(test_path):
|
||||||
file_foder_path = os.path.join(logs_dir_path, test_path_relative_path)
|
file_foder_path = os.path.join(logs_dir_path, test_path_relative_path)
|
||||||
@@ -692,7 +688,7 @@ def prepare_dump_json_file_abs_path(project_mapping, tag_name):
|
|||||||
|
|
||||||
|
|
||||||
def dump_logs(json_data, project_mapping, tag_name):
|
def dump_logs(json_data, project_mapping, tag_name):
|
||||||
""" dump tests data to json file.
|
"""dump tests data to json file.
|
||||||
the dumped file is located in PWD/logs folder.
|
the dumped file is located in PWD/logs folder.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -711,7 +707,11 @@ def get_python2_retire_msg():
|
|||||||
left_days = (retire_day - today).days
|
left_days = (retire_day - today).days
|
||||||
|
|
||||||
if left_days > 0:
|
if left_days > 0:
|
||||||
retire_msg = "Python 2 will retire in {} days, why not move to Python 3?".format(left_days)
|
retire_msg = (
|
||||||
|
"Python 2 will retire in {} days, why not move to Python 3?".format(
|
||||||
|
left_days
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
retire_msg = "Python 2 has been retired, you should move to Python 3."
|
retire_msg = "Python 2 has been retired, you should move to Python 3."
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class Validator(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session_context, resp_obj):
|
def __init__(self, session_context, resp_obj):
|
||||||
""" initialize a Validator for each teststep (API request)
|
"""initialize a Validator for each teststep (API request)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
session_context: HttpRunner session context
|
session_context: HttpRunner session context
|
||||||
@@ -27,7 +27,7 @@ class Validator(object):
|
|||||||
self.validation_results = {}
|
self.validation_results = {}
|
||||||
|
|
||||||
def __eval_validator_check(self, check_item):
|
def __eval_validator_check(self, check_item):
|
||||||
""" evaluate check item in validator.
|
"""evaluate check item in validator.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
check_item: check_item should only be the following 5 formats:
|
check_item: check_item should only be the following 5 formats:
|
||||||
@@ -38,8 +38,9 @@ class Validator(object):
|
|||||||
5, regex string, e.g. "LB[\d]*(.*)RB[\d]*"
|
5, regex string, e.g. "LB[\d]*(.*)RB[\d]*"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(check_item, (dict, list)) \
|
if isinstance(check_item, (dict, list)) or isinstance(
|
||||||
or isinstance(check_item, parser.LazyString):
|
check_item, parser.LazyString
|
||||||
|
):
|
||||||
# format 1/2/3
|
# format 1/2/3
|
||||||
check_value = self.session_context.eval_content(check_item)
|
check_value = self.session_context.eval_content(check_item)
|
||||||
else:
|
else:
|
||||||
@@ -49,7 +50,7 @@ class Validator(object):
|
|||||||
return check_value
|
return check_value
|
||||||
|
|
||||||
def __eval_validator_expect(self, expect_item):
|
def __eval_validator_expect(self, expect_item):
|
||||||
""" evaluate expect item in validator.
|
"""evaluate expect item in validator.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
expect_item: expect_item should only be in 2 types:
|
expect_item: expect_item should only be in 2 types:
|
||||||
@@ -61,12 +62,11 @@ class Validator(object):
|
|||||||
return expect_value
|
return expect_value
|
||||||
|
|
||||||
def validate_script(self, script):
|
def validate_script(self, script):
|
||||||
""" make validation with python script
|
"""make validation with python script"""
|
||||||
"""
|
|
||||||
result = {
|
result = {
|
||||||
"validate_script": "<br/>".join(script),
|
"validate_script": "<br/>".join(script),
|
||||||
"check_result": "pass",
|
"check_result": "pass",
|
||||||
"output": ""
|
"output": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
script = "\n ".join(script)
|
script = "\n ".join(script)
|
||||||
@@ -75,12 +75,14 @@ class Validator(object):
|
|||||||
|
|
||||||
def run_validate_script():
|
def run_validate_script():
|
||||||
{}
|
{}
|
||||||
""".format(script)
|
""".format(
|
||||||
|
script
|
||||||
|
)
|
||||||
|
|
||||||
variables = {
|
variables = {
|
||||||
"status_code": self.resp_obj.status_code,
|
"status_code": self.resp_obj.status_code,
|
||||||
"response_json": self.resp_obj.json,
|
"response_json": self.resp_obj.json,
|
||||||
"response": self.resp_obj
|
"response": self.resp_obj,
|
||||||
}
|
}
|
||||||
variables.update(self.session_context.test_variables_mapping)
|
variables.update(self.session_context.test_variables_mapping)
|
||||||
variables.update(globals())
|
variables.update(globals())
|
||||||
@@ -90,11 +92,13 @@ def run_validate_script():
|
|||||||
except SyntaxError as ex:
|
except SyntaxError as ex:
|
||||||
logger.log_warning("SyntaxError in python validate script: {}".format(ex))
|
logger.log_warning("SyntaxError in python validate script: {}".format(ex))
|
||||||
result["check_result"] = "fail"
|
result["check_result"] = "fail"
|
||||||
result["output"] = "<br/>".join([
|
result["output"] = "<br/>".join(
|
||||||
"ErrorMessage: {}".format(ex.msg),
|
[
|
||||||
"ErrorLine: {}".format(ex.lineno),
|
"ErrorMessage: {}".format(ex.msg),
|
||||||
"ErrorText: {}".format(ex.text)
|
"ErrorLine: {}".format(ex.lineno),
|
||||||
])
|
"ErrorText: {}".format(ex.text),
|
||||||
|
]
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -117,16 +121,17 @@ def run_validate_script():
|
|||||||
else:
|
else:
|
||||||
line_no = "N/A"
|
line_no = "N/A"
|
||||||
|
|
||||||
result["output"] = "<br/>".join([
|
result["output"] = "<br/>".join(
|
||||||
"ErrorType: {}".format(_type.__name__),
|
[
|
||||||
"ErrorLine: {}".format(line_no)
|
"ErrorType: {}".format(_type.__name__),
|
||||||
])
|
"ErrorLine: {}".format(line_no),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def validate(self, validators):
|
def validate(self, validators):
|
||||||
""" make validation with comparators
|
"""make validation with comparators"""
|
||||||
"""
|
|
||||||
self.validation_results = {}
|
self.validation_results = {}
|
||||||
if not validators:
|
if not validators:
|
||||||
return
|
return
|
||||||
@@ -154,7 +159,8 @@ def run_validate_script():
|
|||||||
# validator should be LazyFunction object
|
# validator should be LazyFunction object
|
||||||
if not isinstance(validator, parser.LazyFunction):
|
if not isinstance(validator, parser.LazyFunction):
|
||||||
raise exceptions.ValidationFailure(
|
raise exceptions.ValidationFailure(
|
||||||
"validator should be parsed first: {}".format(validators))
|
"validator should be parsed first: {}".format(validators)
|
||||||
|
)
|
||||||
|
|
||||||
# evaluate validator args with context variable mapping.
|
# evaluate validator args with context variable mapping.
|
||||||
validator_args = validator.get_args()
|
validator_args = validator.get_args()
|
||||||
@@ -169,13 +175,10 @@ def run_validate_script():
|
|||||||
"check": check_item,
|
"check": check_item,
|
||||||
"check_value": check_value,
|
"check_value": check_value,
|
||||||
"expect": expect_item,
|
"expect": expect_item,
|
||||||
"expect_value": expect_value
|
"expect_value": expect_value,
|
||||||
}
|
}
|
||||||
validate_msg = "\nvalidate: {} {} {}({})".format(
|
validate_msg = "\nvalidate: {} {} {}({})".format(
|
||||||
check_item,
|
check_item, comparator, expect_value, type(expect_value).__name__
|
||||||
comparator,
|
|
||||||
expect_value,
|
|
||||||
type(expect_value).__name__
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -192,7 +195,7 @@ def run_validate_script():
|
|||||||
type(check_value).__name__,
|
type(check_value).__name__,
|
||||||
comparator,
|
comparator,
|
||||||
expect_value,
|
expect_value,
|
||||||
type(expect_value).__name__
|
type(expect_value).__name__,
|
||||||
)
|
)
|
||||||
logger.log_error(validate_msg)
|
logger.log_error(validate_msg)
|
||||||
failures.append(validate_msg)
|
failures.append(validate_msg)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from httprunner.builtin.functions import gen_random_string
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from httpbin import app as httpbin_app
|
from httpbin import app as httpbin_app
|
||||||
|
|
||||||
HTTPBIN_HOST = "127.0.0.1"
|
HTTPBIN_HOST = "127.0.0.1"
|
||||||
HTTPBIN_PORT = 3458
|
HTTPBIN_PORT = 3458
|
||||||
HTTPBIN_SERVER = "http://{}:{}".format(HTTPBIN_HOST, HTTPBIN_PORT)
|
HTTPBIN_SERVER = "http://{}:{}".format(HTTPBIN_HOST, HTTPBIN_PORT)
|
||||||
@@ -49,37 +50,30 @@ token_dict = {}
|
|||||||
|
|
||||||
|
|
||||||
def get_sign(*args):
|
def get_sign(*args):
|
||||||
content = ''.join(args).encode('ascii')
|
content = "".join(args).encode("ascii")
|
||||||
sign_key = SECRET_KEY.encode('ascii')
|
sign_key = SECRET_KEY.encode("ascii")
|
||||||
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
||||||
return sign
|
return sign
|
||||||
|
|
||||||
|
|
||||||
def gen_md5(*args):
|
def gen_md5(*args):
|
||||||
return hashlib.md5("".join(args).encode('utf-8')).hexdigest()
|
return hashlib.md5("".join(args).encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def validate_request(func):
|
def validate_request(func):
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
device_sn = request.headers.get('device_sn', "")
|
device_sn = request.headers.get("device_sn", "")
|
||||||
token = request.headers.get('token', "")
|
token = request.headers.get("token", "")
|
||||||
|
|
||||||
if not device_sn or not token:
|
if not device_sn or not token:
|
||||||
result = {
|
result = {"success": False, "msg": "device_sn or token is null."}
|
||||||
'success': False,
|
|
||||||
'msg': "device_sn or token is null."
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), 401)
|
response = make_response(json.dumps(result), 401)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
if token_dict[device_sn] != token:
|
if token_dict[device_sn] != token:
|
||||||
result = {
|
result = {"success": False, "msg": "Authorization failed!"}
|
||||||
'success': False,
|
|
||||||
'msg': "Authorization failed!"
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), 403)
|
response = make_response(json.dumps(result), 403)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
@@ -89,82 +83,64 @@ def validate_request(func):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return "Hello World!"
|
return "Hello World!"
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/get-token', methods=['POST'])
|
@app.route("/api/get-token", methods=["POST"])
|
||||||
def get_token():
|
def get_token():
|
||||||
device_sn = request.headers.get('device_sn', "")
|
device_sn = request.headers.get("device_sn", "")
|
||||||
os_platform = request.headers.get('os_platform', "")
|
os_platform = request.headers.get("os_platform", "")
|
||||||
app_version = request.headers.get('app_version', "")
|
app_version = request.headers.get("app_version", "")
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
sign = data.get('sign', "")
|
sign = data.get("sign", "")
|
||||||
|
|
||||||
expected_sign = get_sign(device_sn, os_platform, app_version)
|
expected_sign = get_sign(device_sn, os_platform, app_version)
|
||||||
if expected_sign != sign:
|
if expected_sign != sign:
|
||||||
result = {
|
result = {"success": False, "msg": "Authorization failed!"}
|
||||||
'success': False,
|
|
||||||
'msg': "Authorization failed!"
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), 403)
|
response = make_response(json.dumps(result), 403)
|
||||||
else:
|
else:
|
||||||
token = gen_random_string(16)
|
token = gen_random_string(16)
|
||||||
token_dict[device_sn] = token
|
token_dict[device_sn] = token
|
||||||
|
|
||||||
result = {
|
result = {"success": True, "token": token}
|
||||||
'success': True,
|
|
||||||
'token': token
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result))
|
response = make_response(json.dumps(result))
|
||||||
|
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/users')
|
@app.route("/api/users")
|
||||||
@validate_request
|
@validate_request
|
||||||
def get_users():
|
def get_users():
|
||||||
users_list = [user for uid, user in users_dict.items()]
|
users_list = [user for uid, user in users_dict.items()]
|
||||||
users = {
|
users = {"success": True, "count": len(users_list), "items": users_list}
|
||||||
'success': True,
|
|
||||||
'count': len(users_list),
|
|
||||||
'items': users_list
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(users))
|
response = make_response(json.dumps(users))
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/reset-all')
|
@app.route("/api/reset-all")
|
||||||
@validate_request
|
@validate_request
|
||||||
def clear_users():
|
def clear_users():
|
||||||
users_dict.clear()
|
users_dict.clear()
|
||||||
result = {
|
result = {"success": True}
|
||||||
'success': True
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result))
|
response = make_response(json.dumps(result))
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>', methods=['POST'])
|
@app.route("/api/users/<int:uid>", methods=["POST"])
|
||||||
@validate_request
|
@validate_request
|
||||||
def create_user(uid):
|
def create_user(uid):
|
||||||
user = request.get_json()
|
user = request.get_json()
|
||||||
if uid not in users_dict:
|
if uid not in users_dict:
|
||||||
result = {
|
result = {"success": True, "msg": "user created successfully."}
|
||||||
'success': True,
|
|
||||||
'msg': "user created successfully."
|
|
||||||
}
|
|
||||||
status_code = 201
|
status_code = 201
|
||||||
users_dict[uid] = user
|
users_dict[uid] = user
|
||||||
else:
|
else:
|
||||||
result = {
|
result = {"success": False, "msg": "user already existed."}
|
||||||
'success': False,
|
|
||||||
'msg': "user already existed."
|
|
||||||
}
|
|
||||||
status_code = 500
|
status_code = 500
|
||||||
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
@@ -172,21 +148,15 @@ def create_user(uid):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>')
|
@app.route("/api/users/<int:uid>")
|
||||||
@validate_request
|
@validate_request
|
||||||
def get_user(uid):
|
def get_user(uid):
|
||||||
user = users_dict.get(uid, {})
|
user = users_dict.get(uid, {})
|
||||||
if user:
|
if user:
|
||||||
result = {
|
result = {"success": True, "data": user}
|
||||||
'success': True,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
status_code = 200
|
status_code = 200
|
||||||
else:
|
else:
|
||||||
result = {
|
result = {"success": False, "data": user}
|
||||||
'success': False,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
status_code = 404
|
status_code = 404
|
||||||
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
@@ -194,7 +164,7 @@ def get_user(uid):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>', methods=['PUT'])
|
@app.route("/api/users/<int:uid>", methods=["PUT"])
|
||||||
@validate_request
|
@validate_request
|
||||||
def update_user(uid):
|
def update_user(uid):
|
||||||
user = users_dict.get(uid, {})
|
user = users_dict.get(uid, {})
|
||||||
@@ -207,16 +177,13 @@ def update_user(uid):
|
|||||||
success = False
|
success = False
|
||||||
status_code = 404
|
status_code = 404
|
||||||
|
|
||||||
result = {
|
result = {"success": success, "data": user}
|
||||||
'success': success,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/users/<int:uid>', methods=['DELETE'])
|
@app.route("/api/users/<int:uid>", methods=["DELETE"])
|
||||||
@validate_request
|
@validate_request
|
||||||
def delete_user(uid):
|
def delete_user(uid):
|
||||||
user = users_dict.pop(uid, {})
|
user = users_dict.pop(uid, {})
|
||||||
@@ -227,10 +194,7 @@ def delete_user(uid):
|
|||||||
success = False
|
success = False
|
||||||
status_code = 404
|
status_code = 404
|
||||||
|
|
||||||
result = {
|
result = {"success": success, "data": user}
|
||||||
'success': success,
|
|
||||||
'data': user
|
|
||||||
}
|
|
||||||
response = make_response(json.dumps(result), status_code)
|
response = make_response(json.dumps(result), status_code)
|
||||||
response.headers["Content-Type"] = "application/json"
|
response.headers["Content-Type"] = "application/json"
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -19,18 +19,13 @@ def run_httpbin():
|
|||||||
|
|
||||||
|
|
||||||
class ApiServerUnittest(unittest.TestCase):
|
class ApiServerUnittest(unittest.TestCase):
|
||||||
""" Test case class that sets up an HTTP server which can be used within the tests
|
"""Test case class that sets up an HTTP server which can be used within the tests"""
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.host = "http://127.0.0.1:5000"
|
cls.host = "http://127.0.0.1:5000"
|
||||||
cls.flask_process = multiprocessing.Process(
|
cls.flask_process = multiprocessing.Process(target=run_flask)
|
||||||
target=run_flask
|
cls.httpbin_process = multiprocessing.Process(target=run_httpbin)
|
||||||
)
|
|
||||||
cls.httpbin_process = multiprocessing.Process(
|
|
||||||
target=run_httpbin
|
|
||||||
)
|
|
||||||
cls.flask_process.start()
|
cls.flask_process.start()
|
||||||
cls.httpbin_process.start()
|
cls.httpbin_process.start()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@@ -44,15 +39,13 @@ class ApiServerUnittest(unittest.TestCase):
|
|||||||
def get_token(self, user_agent, device_sn, os_platform, app_version):
|
def get_token(self, user_agent, device_sn, os_platform, app_version):
|
||||||
url = "%s/api/get-token" % self.host
|
url = "%s/api/get-token" % self.host
|
||||||
headers = {
|
headers = {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
'User-Agent': user_agent,
|
"User-Agent": user_agent,
|
||||||
'device_sn': device_sn,
|
"device_sn": device_sn,
|
||||||
'os_platform': os_platform,
|
"os_platform": os_platform,
|
||||||
'app_version': app_version
|
"app_version": app_version,
|
||||||
}
|
|
||||||
data = {
|
|
||||||
'sign': get_sign(device_sn, os_platform, app_version)
|
|
||||||
}
|
}
|
||||||
|
data = {"sign": get_sign(device_sn, os_platform, app_version)}
|
||||||
|
|
||||||
resp = self.api_client.post(url, json=data, headers=headers)
|
resp = self.api_client.post(url, json=data, headers=headers)
|
||||||
resp_json = resp.json()
|
resp_json = resp.json()
|
||||||
@@ -62,14 +55,11 @@ class ApiServerUnittest(unittest.TestCase):
|
|||||||
return resp_json["token"]
|
return resp_json["token"]
|
||||||
|
|
||||||
def get_authenticated_headers(self):
|
def get_authenticated_headers(self):
|
||||||
user_agent = 'iOS/10.3'
|
user_agent = "iOS/10.3"
|
||||||
device_sn = gen_random_string(15)
|
device_sn = gen_random_string(15)
|
||||||
os_platform = 'ios'
|
os_platform = "ios"
|
||||||
app_version = '2.8.6'
|
app_version = "2.8.6"
|
||||||
|
|
||||||
token = self.get_token(user_agent, device_sn, os_platform, app_version)
|
token = self.get_token(user_agent, device_sn, os_platform, app_version)
|
||||||
headers = {
|
headers = {"device_sn": device_sn, "token": token}
|
||||||
'device_sn': device_sn,
|
|
||||||
'token': token
|
|
||||||
}
|
|
||||||
return headers
|
return headers
|
||||||
|
|||||||
@@ -17,12 +17,7 @@ def get_base_url():
|
|||||||
|
|
||||||
|
|
||||||
def get_default_request():
|
def get_default_request():
|
||||||
return {
|
return {"base_url": BASE_URL, "headers": {"content-type": "application/json"}}
|
||||||
"base_url": BASE_URL,
|
|
||||||
"headers": {
|
|
||||||
"content-type": "application/json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def sum_two(m, n):
|
def sum_two(m, n):
|
||||||
@@ -30,8 +25,8 @@ def sum_two(m, n):
|
|||||||
|
|
||||||
|
|
||||||
def sum_status_code(status_code, expect_sum):
|
def sum_status_code(status_code, expect_sum):
|
||||||
""" sum status code digits
|
"""sum status code digits
|
||||||
e.g. 400 => 4, 201 => 3
|
e.g. 400 => 4, 201 => 3
|
||||||
"""
|
"""
|
||||||
sum_value = 0
|
sum_value = 0
|
||||||
for digit in str(status_code):
|
for digit in str(status_code):
|
||||||
@@ -48,8 +43,7 @@ os.environ["TEST_ENV"] = "PRODUCTION"
|
|||||||
|
|
||||||
|
|
||||||
def skip_test_in_production_env():
|
def skip_test_in_production_env():
|
||||||
""" skip this test in production environment
|
"""skip this test in production environment"""
|
||||||
"""
|
|
||||||
return os.environ["TEST_ENV"] == "PRODUCTION"
|
return os.environ["TEST_ENV"] == "PRODUCTION"
|
||||||
|
|
||||||
|
|
||||||
@@ -58,16 +52,13 @@ def get_user_agent():
|
|||||||
|
|
||||||
|
|
||||||
def gen_app_version():
|
def gen_app_version():
|
||||||
return [
|
return [{"app_version": "2.8.5"}, {"app_version": "2.8.6"}]
|
||||||
{"app_version": "2.8.5"},
|
|
||||||
{"app_version": "2.8.6"}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def get_account():
|
def get_account():
|
||||||
return [
|
return [
|
||||||
{"username": "user1", "password": "111111"},
|
{"username": "user1", "password": "111111"},
|
||||||
{"username": "user2", "password": "222222"}
|
{"username": "user2", "password": "222222"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -81,7 +72,7 @@ def gen_random_string(str_len):
|
|||||||
random_char = random.choice(string.ascii_letters + string.digits)
|
random_char = random.choice(string.ascii_letters + string.digits)
|
||||||
random_char_list.append(random_char)
|
random_char_list.append(random_char)
|
||||||
|
|
||||||
random_string = ''.join(random_char_list)
|
random_string = "".join(random_char_list)
|
||||||
return random_string
|
return random_string
|
||||||
|
|
||||||
|
|
||||||
@@ -94,8 +85,7 @@ def setup_hook_remove_kwargs(request):
|
|||||||
|
|
||||||
|
|
||||||
def teardown_hook_sleep_N_secs(response, n_secs):
|
def teardown_hook_sleep_N_secs(response, n_secs):
|
||||||
""" sleep n seconds after request
|
"""sleep n seconds after request"""
|
||||||
"""
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
else:
|
else:
|
||||||
@@ -113,9 +103,11 @@ def modify_request_json(request, os_platform):
|
|||||||
def setup_hook_httpntlmauth(request):
|
def setup_hook_httpntlmauth(request):
|
||||||
if "httpntlmauth" in request:
|
if "httpntlmauth" in request:
|
||||||
from requests_ntlm import HttpNtlmAuth
|
from requests_ntlm import HttpNtlmAuth
|
||||||
|
|
||||||
auth_account = request.pop("httpntlmauth")
|
auth_account = request.pop("httpntlmauth")
|
||||||
request["auth"] = HttpNtlmAuth(
|
request["auth"] = HttpNtlmAuth(
|
||||||
auth_account["username"], auth_account["password"])
|
auth_account["username"], auth_account["password"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def alter_response(response):
|
def alter_response(response):
|
||||||
@@ -123,18 +115,15 @@ def alter_response(response):
|
|||||||
response.headers["Content-Type"] = "html/text"
|
response.headers["Content-Type"] = "html/text"
|
||||||
response.json["headers"]["Host"] = "127.0.0.1:8888"
|
response.json["headers"]["Host"] = "127.0.0.1:8888"
|
||||||
response.new_attribute = "new_attribute_value"
|
response.new_attribute = "new_attribute_value"
|
||||||
response.new_attribute_dict = {
|
response.new_attribute_dict = {"key": 123}
|
||||||
"key": 123
|
|
||||||
}
|
|
||||||
|
|
||||||
def alter_response_302(response):
|
def alter_response_302(response):
|
||||||
response.status_code = 500
|
response.status_code = 500
|
||||||
response.headers["Content-Type"] = "html/text"
|
response.headers["Content-Type"] = "html/text"
|
||||||
response.text = "abcdef"
|
response.text = "abcdef"
|
||||||
response.new_attribute = "new_attribute_value"
|
response.new_attribute = "new_attribute_value"
|
||||||
response.new_attribute_dict = {
|
response.new_attribute_dict = {"key": 123}
|
||||||
"key": 123
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def alter_response_error(response):
|
def alter_response_error(response):
|
||||||
@@ -143,7 +132,4 @@ def alter_response_error(response):
|
|||||||
|
|
||||||
|
|
||||||
def gen_variables():
|
def gen_variables():
|
||||||
return {
|
return {"var_a": 1, "var_b": 2}
|
||||||
"var_a": 1,
|
|
||||||
"var_b": 2
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,64 +11,69 @@ from tests.base import ApiServerUnittest
|
|||||||
|
|
||||||
|
|
||||||
class TestHttpRunner(ApiServerUnittest):
|
class TestHttpRunner(ApiServerUnittest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.testcase_cli_path = "tests/data/demo_testcase_cli.yml"
|
self.testcase_cli_path = "tests/data/demo_testcase_cli.yml"
|
||||||
self.testcase_file_path_list = [
|
self.testcase_file_path_list = [
|
||||||
os.path.join(
|
os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.yml"),
|
||||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.yml'),
|
os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.json"),
|
||||||
os.path.join(
|
|
||||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.json')
|
|
||||||
]
|
]
|
||||||
testcases = [{
|
testcases = [
|
||||||
'config': {
|
{
|
||||||
'name': 'testcase description',
|
"config": {
|
||||||
'request': {
|
"name": "testcase description",
|
||||||
'base_url': '',
|
"request": {
|
||||||
'headers': {'User-Agent': 'python-requests/2.18.4'}
|
"base_url": "",
|
||||||
},
|
"headers": {"User-Agent": "python-requests/2.18.4"},
|
||||||
'variables': []
|
|
||||||
},
|
|
||||||
"teststeps": [
|
|
||||||
{
|
|
||||||
'name': '/api/get-token',
|
|
||||||
'request': {
|
|
||||||
'url': 'http://127.0.0.1:5000/api/get-token',
|
|
||||||
'method': 'POST',
|
|
||||||
'headers': {'Content-Type': 'application/json', 'app_version': '2.8.6',
|
|
||||||
'device_sn': 'FwgRiO7CNA50DSU', 'os_platform': 'ios', 'user_agent': 'iOS/10.3'},
|
|
||||||
'json': {'sign': '9c0c7e51c91ae963c833a4ccbab8d683c4a90c98'}
|
|
||||||
},
|
},
|
||||||
'extract': [
|
"variables": [],
|
||||||
{'token': 'content.token'}
|
|
||||||
],
|
|
||||||
'validate': [
|
|
||||||
{'eq': ['status_code', 200]},
|
|
||||||
{'eq': ['headers.Content-Type', 'application/json']},
|
|
||||||
{'eq': ['content.success', True]}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
"teststeps": [
|
||||||
'name': '/api/users/1000',
|
{
|
||||||
'request': {
|
"name": "/api/get-token",
|
||||||
'url': 'http://127.0.0.1:5000/api/users/1000',
|
"request": {
|
||||||
'method': 'POST',
|
"url": "http://127.0.0.1:5000/api/get-token",
|
||||||
'headers': {'Content-Type': 'application/json',
|
"method": "POST",
|
||||||
'device_sn': 'FwgRiO7CNA50DSU','token': '$token'},
|
"headers": {
|
||||||
'json': {'name': 'user1', 'password': '123456'}
|
"Content-Type": "application/json",
|
||||||
|
"app_version": "2.8.6",
|
||||||
|
"device_sn": "FwgRiO7CNA50DSU",
|
||||||
|
"os_platform": "ios",
|
||||||
|
"user_agent": "iOS/10.3",
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"sign": "9c0c7e51c91ae963c833a4ccbab8d683c4a90c98"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"extract": [{"token": "content.token"}],
|
||||||
|
"validate": [
|
||||||
|
{"eq": ["status_code", 200]},
|
||||||
|
{"eq": ["headers.Content-Type", "application/json"]},
|
||||||
|
{"eq": ["content.success", True]},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'validate': [
|
{
|
||||||
{'eq': ['status_code', 201]},
|
"name": "/api/users/1000",
|
||||||
{'eq': ['headers.Content-Type', 'application/json']},
|
"request": {
|
||||||
{'eq': ['content.success', True]},
|
"url": "http://127.0.0.1:5000/api/users/1000",
|
||||||
{'eq': ['content.msg', 'user created successfully.']}
|
"method": "POST",
|
||||||
]
|
"headers": {
|
||||||
}
|
"Content-Type": "application/json",
|
||||||
]
|
"device_sn": "FwgRiO7CNA50DSU",
|
||||||
}]
|
"token": "$token",
|
||||||
self.tests_mapping = {
|
},
|
||||||
"testcases": testcases
|
"json": {"name": "user1", "password": "123456"},
|
||||||
}
|
},
|
||||||
|
"validate": [
|
||||||
|
{"eq": ["status_code", 201]},
|
||||||
|
{"eq": ["headers.Content-Type", "application/json"]},
|
||||||
|
{"eq": ["content.success", True]},
|
||||||
|
{"eq": ["content.msg", "user created successfully."]},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
self.tests_mapping = {"testcases": testcases}
|
||||||
self.runner = HttpRunner(failfast=True)
|
self.runner = HttpRunner(failfast=True)
|
||||||
self.reset_all()
|
self.reset_all()
|
||||||
|
|
||||||
@@ -85,10 +90,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
def test_text_run_times_invalid(self):
|
def test_text_run_times_invalid(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "post data", "variables": []},
|
||||||
'name': "post data",
|
|
||||||
'variables': []
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "post data",
|
"name": "post data",
|
||||||
@@ -98,20 +100,16 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {
|
||||||
"User-Agent": "python-requests/2.18.4",
|
"User-Agent": "python-requests/2.18.4",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
"data": "abc"
|
"data": "abc",
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"eq": ["status_code", 200]}],
|
||||||
{"eq": ["status_code", 200]}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {"testcases": testcases}
|
||||||
"testcases": testcases
|
|
||||||
}
|
|
||||||
with self.assertRaises(exceptions.ParamsError):
|
with self.assertRaises(exceptions.ParamsError):
|
||||||
self.runner.run_tests(tests_mapping)
|
self.runner.run_tests(tests_mapping)
|
||||||
|
|
||||||
@@ -123,12 +121,9 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
'name': "post data",
|
"name": "post data",
|
||||||
'variables': {
|
"variables": {"var1": "abc", "var2": "def"},
|
||||||
"var1": "abc",
|
"export": ["status_code", "req_data"],
|
||||||
"var2": "def"
|
|
||||||
},
|
|
||||||
"export": ["status_code", "req_data"]
|
|
||||||
},
|
},
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
@@ -138,24 +133,20 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {
|
||||||
"User-Agent": "python-requests/2.18.4",
|
"User-Agent": "python-requests/2.18.4",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
"data": "$var1"
|
"data": "$var1",
|
||||||
},
|
},
|
||||||
"extract": {
|
"extract": {
|
||||||
"status_code": "status_code",
|
"status_code": "status_code",
|
||||||
"req_data": "content.data"
|
"req_data": "content.data",
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"eq": ["status_code", 200]}],
|
||||||
{"eq": ["status_code", 200]}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {"testcases": testcases}
|
||||||
"testcases": testcases
|
|
||||||
}
|
|
||||||
self.runner.run_tests(tests_mapping)
|
self.runner.run_tests(tests_mapping)
|
||||||
vars_out = self.runner.get_vars_out()
|
vars_out = self.runner.get_vars_out()
|
||||||
self.assertIsInstance(vars_out, list)
|
self.assertIsInstance(vars_out, list)
|
||||||
@@ -166,7 +157,8 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
|
|
||||||
def test_save_variables_output_with_parameters(self):
|
def test_save_variables_output_with_parameters(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/testsuites/create_users_with_parameters.yml')
|
os.getcwd(), "tests/testsuites/create_users_with_parameters.yml"
|
||||||
|
)
|
||||||
self.runner.run(testcase_file_path)
|
self.runner.run(testcase_file_path)
|
||||||
vars_out = self.runner.get_vars_out()
|
vars_out = self.runner.get_vars_out()
|
||||||
self.assertIsInstance(vars_out, list)
|
self.assertIsInstance(vars_out, list)
|
||||||
@@ -188,7 +180,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 10)
|
self.assertEqual(summary["stat"]["teststeps"]["total"], 10)
|
||||||
self.assertEqual(summary["stat"]["teststeps"]["skipped"], 4)
|
self.assertEqual(summary["stat"]["teststeps"]["skipped"], 4)
|
||||||
|
|
||||||
report_save_dir = os.path.join(os.getcwd(), 'reports', "demo")
|
report_save_dir = os.path.join(os.getcwd(), "reports", "demo")
|
||||||
report.gen_html_report(summary, report_dir=report_save_dir)
|
report.gen_html_report(summary, report_dir=report_save_dir)
|
||||||
self.assertGreater(len(os.listdir(report_save_dir)), 0)
|
self.assertGreater(len(os.listdir(report_save_dir)), 0)
|
||||||
shutil.rmtree(report_save_dir)
|
shutil.rmtree(report_save_dir)
|
||||||
@@ -200,7 +192,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 10)
|
self.assertEqual(summary["stat"]["teststeps"]["total"], 10)
|
||||||
self.assertEqual(summary["stat"]["teststeps"]["skipped"], 4)
|
self.assertEqual(summary["stat"]["teststeps"]["skipped"], 4)
|
||||||
|
|
||||||
report_file = os.path.join(os.getcwd(), 'reports', "demo", "test.html")
|
report_file = os.path.join(os.getcwd(), "reports", "demo", "test.html")
|
||||||
report.gen_html_report(summary, report_file=report_file)
|
report.gen_html_report(summary, report_file=report_file)
|
||||||
report_save_dir = os.path.dirname(report_file)
|
report_save_dir = os.path.dirname(report_file)
|
||||||
self.assertEqual(len(os.listdir(report_save_dir)), 1)
|
self.assertEqual(len(os.listdir(report_save_dir)), 1)
|
||||||
@@ -208,7 +200,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
shutil.rmtree(report_save_dir)
|
shutil.rmtree(report_save_dir)
|
||||||
|
|
||||||
def test_log_file(self):
|
def test_log_file(self):
|
||||||
log_file_path = os.path.join(os.getcwd(), 'reports', "test_log_file.log")
|
log_file_path = os.path.join(os.getcwd(), "reports", "test_log_file.log")
|
||||||
runner = HttpRunner(failfast=True, log_file=log_file_path)
|
runner = HttpRunner(failfast=True, log_file=log_file_path)
|
||||||
runner.run(self.testcase_cli_path)
|
runner.run(self.testcase_cli_path)
|
||||||
self.assertTrue(os.path.isfile(log_file_path))
|
self.assertTrue(os.path.isfile(log_file_path))
|
||||||
@@ -223,10 +215,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
self.assertIn("records", summary["details"][0])
|
self.assertIn("records", summary["details"][0])
|
||||||
|
|
||||||
def test_run_yaml_upload(self):
|
def test_run_yaml_upload(self):
|
||||||
upload_cases_list = [
|
upload_cases_list = ["tests/httpbin/upload.yml", "tests/httpbin/upload.v2.yml"]
|
||||||
"tests/httpbin/upload.yml",
|
|
||||||
"tests/httpbin/upload.v2.yml"
|
|
||||||
]
|
|
||||||
for upload_case in upload_cases_list:
|
for upload_case in upload_cases_list:
|
||||||
summary = self.runner.run(upload_case)
|
summary = self.runner.run(upload_case)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
@@ -238,10 +227,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
def test_run_post_data(self):
|
def test_run_post_data(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "post data", "variables": []},
|
||||||
'name': "post data",
|
|
||||||
'variables': []
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "post data",
|
"name": "post data",
|
||||||
@@ -250,35 +236,32 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {
|
||||||
"User-Agent": "python-requests/2.18.4",
|
"User-Agent": "python-requests/2.18.4",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
"data": "abc"
|
"data": "abc",
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"eq": ["status_code", 200]}],
|
||||||
{"eq": ["status_code", 200]}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {"testcases": testcases}
|
||||||
"testcases": testcases
|
|
||||||
}
|
|
||||||
summary = self.runner.run_tests(tests_mapping)
|
summary = self.runner.run_tests(tests_mapping)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
self.assertEqual(summary["stat"]["testcases"]["total"], 1)
|
self.assertEqual(summary["stat"]["testcases"]["total"], 1)
|
||||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 1)
|
self.assertEqual(summary["stat"]["teststeps"]["total"], 1)
|
||||||
resp_json = json.loads(summary["details"][0]["records"][0]["meta_datas"]["data"][0]["response"]["body"])
|
resp_json = json.loads(
|
||||||
self.assertEqual(
|
summary["details"][0]["records"][0]["meta_datas"]["data"][0]["response"][
|
||||||
resp_json["data"],
|
"body"
|
||||||
"abc"
|
]
|
||||||
)
|
)
|
||||||
|
self.assertEqual(resp_json["data"], "abc")
|
||||||
|
|
||||||
def test_html_report_repsonse_image(self):
|
def test_html_report_repsonse_image(self):
|
||||||
runner = HttpRunner(failfast=True)
|
runner = HttpRunner(failfast=True)
|
||||||
summary = runner.run("tests/httpbin/load_image.yml")
|
summary = runner.run("tests/httpbin/load_image.yml")
|
||||||
|
|
||||||
report_save_dir = os.path.join(os.getcwd(), 'reports', "demo")
|
report_save_dir = os.path.join(os.getcwd(), "reports", "demo")
|
||||||
report_path = report.gen_html_report(summary, report_dir=report_save_dir)
|
report_path = report.gen_html_report(summary, report_dir=report_save_dir)
|
||||||
self.assertTrue(os.path.isfile(report_path))
|
self.assertTrue(os.path.isfile(report_path))
|
||||||
shutil.rmtree(report_save_dir)
|
shutil.rmtree(report_save_dir)
|
||||||
@@ -286,7 +269,9 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
def test_testcase_layer_with_api(self):
|
def test_testcase_layer_with_api(self):
|
||||||
summary = self.runner.run("tests/testcases/setup.yml")
|
summary = self.runner.run("tests/testcases/setup.yml")
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
self.assertEqual(summary["details"][0]["records"][0]["name"], "get token (setup)")
|
self.assertEqual(
|
||||||
|
summary["details"][0]["records"][0]["name"], "get token (setup)"
|
||||||
|
)
|
||||||
self.assertEqual(summary["stat"]["testcases"]["total"], 1)
|
self.assertEqual(summary["stat"]["testcases"]["total"], 1)
|
||||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 2)
|
self.assertEqual(summary["stat"]["teststeps"]["total"], 2)
|
||||||
|
|
||||||
@@ -301,8 +286,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
self.assertFalse(summary["success"])
|
self.assertFalse(summary["success"])
|
||||||
|
|
||||||
def test_run_httprunner_with_hooks(self):
|
def test_run_httprunner_with_hooks(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(os.getcwd(), "tests/httpbin/hooks.yml")
|
||||||
os.getcwd(), 'tests/httpbin/hooks.yml')
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
summary = self.runner.run(testcase_file_path)
|
summary = self.runner.run(testcase_file_path)
|
||||||
end_time = time.time()
|
end_time = time.time()
|
||||||
@@ -319,11 +303,9 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
"request": {
|
"request": {
|
||||||
"url": "{}/headers".format(HTTPBIN_SERVER),
|
"url": "{}/headers".format(HTTPBIN_SERVER),
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"data": "abc"
|
"data": "abc",
|
||||||
},
|
},
|
||||||
"teardown_hooks": [
|
"teardown_hooks": ["${alter_response($response)}"],
|
||||||
"${alter_response($response)}"
|
|
||||||
],
|
|
||||||
"validate": [
|
"validate": [
|
||||||
{"eq": ["status_code", 500]},
|
{"eq": ["status_code", 500]},
|
||||||
{"eq": ["headers.content-type", "html/text"]},
|
{"eq": ["headers.content-type", "html/text"]},
|
||||||
@@ -332,16 +314,16 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
{"eq": ["text.headers.Host", "127.0.0.1:8888"]},
|
{"eq": ["text.headers.Host", "127.0.0.1:8888"]},
|
||||||
{"eq": ["new_attribute", "new_attribute_value"]},
|
{"eq": ["new_attribute", "new_attribute_value"]},
|
||||||
{"eq": ["new_attribute_dict", {"key": 123}]},
|
{"eq": ["new_attribute_dict", {"key": 123}]},
|
||||||
{"eq": ["new_attribute_dict.key", 123]}
|
{"eq": ["new_attribute_dict.key", 123]},
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": loader.load_project_data("tests"),
|
"project_mapping": loader.load_project_data("tests"),
|
||||||
"testcases": testcases
|
"testcases": testcases,
|
||||||
}
|
}
|
||||||
summary = self.runner.run_tests(tests_mapping)
|
summary = self.runner.run_tests(tests_mapping)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
@@ -349,30 +331,24 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
def test_run_httprunner_with_teardown_hooks_not_exist_attribute(self):
|
def test_run_httprunner_with_teardown_hooks_not_exist_attribute(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "test teardown hooks"},
|
||||||
"name": "test teardown hooks"
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "test teardown hooks",
|
"name": "test teardown hooks",
|
||||||
"request": {
|
"request": {
|
||||||
"url": "{}/headers".format(HTTPBIN_SERVER),
|
"url": "{}/headers".format(HTTPBIN_SERVER),
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"data": "abc"
|
"data": "abc",
|
||||||
},
|
},
|
||||||
"teardown_hooks": [
|
"teardown_hooks": ["${alter_response($response)}"],
|
||||||
"${alter_response($response)}"
|
"validate": [{"eq": ["attribute_not_exist", "new_attribute"]}],
|
||||||
],
|
|
||||||
"validate": [
|
|
||||||
{"eq": ["attribute_not_exist", "new_attribute"]}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": loader.load_project_data("tests"),
|
"project_mapping": loader.load_project_data("tests"),
|
||||||
"testcases": testcases
|
"testcases": testcases,
|
||||||
}
|
}
|
||||||
summary = self.runner.run_tests(tests_mapping)
|
summary = self.runner.run_tests(tests_mapping)
|
||||||
self.assertFalse(summary["success"])
|
self.assertFalse(summary["success"])
|
||||||
@@ -381,27 +357,23 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
def test_run_httprunner_with_teardown_hooks_error(self):
|
def test_run_httprunner_with_teardown_hooks_error(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "test teardown hooks"},
|
||||||
"name": "test teardown hooks"
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "test teardown hooks",
|
"name": "test teardown hooks",
|
||||||
"request": {
|
"request": {
|
||||||
"url": "{}/headers".format(HTTPBIN_SERVER),
|
"url": "{}/headers".format(HTTPBIN_SERVER),
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"data": "abc"
|
"data": "abc",
|
||||||
},
|
},
|
||||||
"teardown_hooks": [
|
"teardown_hooks": ["${alter_response_error($response)}"],
|
||||||
"${alter_response_error($response)}"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": loader.load_project_data("tests"),
|
"project_mapping": loader.load_project_data("tests"),
|
||||||
"testcases": testcases
|
"testcases": testcases,
|
||||||
}
|
}
|
||||||
summary = self.runner.run_tests(tests_mapping)
|
summary = self.runner.run_tests(tests_mapping)
|
||||||
self.assertFalse(summary["success"])
|
self.assertFalse(summary["success"])
|
||||||
@@ -478,29 +450,32 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 3)
|
self.assertEqual(summary["stat"]["teststeps"]["total"], 3)
|
||||||
self.assertEqual(summary["stat"]["teststeps"]["successes"], 3)
|
self.assertEqual(summary["stat"]["teststeps"]["successes"], 3)
|
||||||
|
|
||||||
|
|
||||||
def test_run_testcase_template_variables(self):
|
def test_run_testcase_template_variables(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/demo_testcase_variables.yml')
|
os.getcwd(), "tests/data/demo_testcase_variables.yml"
|
||||||
|
)
|
||||||
summary = self.runner.run(testcase_file_path)
|
summary = self.runner.run(testcase_file_path)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
|
|
||||||
def test_run_testcase_template_import_functions(self):
|
def test_run_testcase_template_import_functions(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/demo_testcase_functions.yml')
|
os.getcwd(), "tests/data/demo_testcase_functions.yml"
|
||||||
|
)
|
||||||
summary = self.runner.run(testcase_file_path)
|
summary = self.runner.run(testcase_file_path)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
|
|
||||||
def test_run_testcase_layered(self):
|
def test_run_testcase_layered(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/demo_testcase_layer.yml')
|
os.getcwd(), "tests/data/demo_testcase_layer.yml"
|
||||||
|
)
|
||||||
summary = self.runner.run(testcase_file_path)
|
summary = self.runner.run(testcase_file_path)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
self.assertEqual(len(summary["details"]), 1)
|
self.assertEqual(len(summary["details"]), 1)
|
||||||
|
|
||||||
def test_run_testcase_output(self):
|
def test_run_testcase_output(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/demo_testcase_layer.yml')
|
os.getcwd(), "tests/data/demo_testcase_layer.yml"
|
||||||
|
)
|
||||||
summary = self.runner.run(testcase_file_path)
|
summary = self.runner.run(testcase_file_path)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
self.assertIn("token", summary["details"][0]["in_out"]["out"])
|
self.assertIn("token", summary["details"][0]["in_out"]["out"])
|
||||||
@@ -509,10 +484,9 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
|
|
||||||
def test_run_testcase_with_variables_mapping(self):
|
def test_run_testcase_with_variables_mapping(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/demo_testcase_layer.yml')
|
os.getcwd(), "tests/data/demo_testcase_layer.yml"
|
||||||
variables_mapping = {
|
)
|
||||||
"app_version": '2.9.7'
|
variables_mapping = {"app_version": "2.9.7"}
|
||||||
}
|
|
||||||
summary = self.runner.run(testcase_file_path, mapping=variables_mapping)
|
summary = self.runner.run(testcase_file_path, mapping=variables_mapping)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
self.assertIn("token", summary["details"][0]["in_out"]["out"])
|
self.assertIn("token", summary["details"][0]["in_out"]["out"])
|
||||||
@@ -521,7 +495,8 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
|
|
||||||
def test_run_testcase_with_parameters(self):
|
def test_run_testcase_with_parameters(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/testsuites/create_users_with_parameters.yml')
|
os.getcwd(), "tests/testsuites/create_users_with_parameters.yml"
|
||||||
|
)
|
||||||
summary = self.runner.run(testcase_file_path)
|
summary = self.runner.run(testcase_file_path)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
self.assertEqual(len(summary["details"]), 3 * 2)
|
self.assertEqual(len(summary["details"]), 3 * 2)
|
||||||
@@ -530,16 +505,13 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 3 * 2 * 2)
|
self.assertEqual(summary["stat"]["teststeps"]["total"], 3 * 2 * 2)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
summary["details"][0]["name"],
|
summary["details"][0]["name"],
|
||||||
"create user 101 and check result for TESTSUITE_X1."
|
"create user 101 and check result for TESTSUITE_X1.",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
summary["details"][5]["name"],
|
summary["details"][5]["name"],
|
||||||
"create user 103 and check result for TESTSUITE_X2."
|
"create user 103 and check result for TESTSUITE_X2.",
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
summary["details"][0]["stat"]["total"],
|
|
||||||
2
|
|
||||||
)
|
)
|
||||||
|
self.assertEqual(summary["details"][0]["stat"]["total"], 2)
|
||||||
records_name_list = [
|
records_name_list = [
|
||||||
summary["details"][i]["records"][1]["meta_datas"][1]["name"]
|
summary["details"][i]["records"][1]["meta_datas"][1]["name"]
|
||||||
for i in range(6)
|
for i in range(6)
|
||||||
@@ -552,59 +524,51 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
"create user 102 for TESTSUITE_X1",
|
"create user 102 for TESTSUITE_X1",
|
||||||
"create user 102 for TESTSUITE_X2",
|
"create user 102 for TESTSUITE_X2",
|
||||||
"create user 103 for TESTSUITE_X1",
|
"create user 103 for TESTSUITE_X1",
|
||||||
"create user 103 for TESTSUITE_X2"
|
"create user 103 for TESTSUITE_X2",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_validate_response_content(self):
|
def test_validate_response_content(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(os.getcwd(), "tests/httpbin/basic.yml")
|
||||||
os.getcwd(), 'tests/httpbin/basic.yml')
|
|
||||||
summary = self.runner.run(testcase_file_path)
|
summary = self.runner.run(testcase_file_path)
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
|
|
||||||
def test_html_report_xss(self):
|
def test_html_report_xss(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "post data"},
|
||||||
'name': "post data"
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "post data",
|
"name": "post data",
|
||||||
"request": {
|
"request": {
|
||||||
"url": "{}/anything".format(HTTPBIN_SERVER),
|
"url": "{}/anything".format(HTTPBIN_SERVER),
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {"Content-Type": "application/json"},
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
"json": {
|
"json": {
|
||||||
'success': False,
|
"success": False,
|
||||||
"person": "<img src=x onerror=alert(1)>"
|
"person": "<img src=x onerror=alert(1)>",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"eq": ["status_code", 200]}],
|
||||||
{"eq": ["status_code", 200]}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {"testcases": testcases}
|
||||||
"testcases": testcases
|
|
||||||
}
|
|
||||||
summary = self.runner.run(tests_mapping)
|
summary = self.runner.run(tests_mapping)
|
||||||
report_path = report.gen_html_report(summary)
|
report_path = report.gen_html_report(summary)
|
||||||
with open(report_path) as f:
|
with open(report_path) as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
m = re.findall(
|
m = re.findall(
|
||||||
re.escape(""person": "<img src=x onerror=alert(1)>""),
|
re.escape(
|
||||||
content
|
""person": "<img src=x onerror=alert(1)>""
|
||||||
|
),
|
||||||
|
content,
|
||||||
)
|
)
|
||||||
self.assertEqual(len(m), 2)
|
self.assertEqual(len(m), 2)
|
||||||
|
|
||||||
|
|
||||||
class TestApi(ApiServerUnittest):
|
class TestApi(ApiServerUnittest):
|
||||||
|
|
||||||
def test_testcase_loader(self):
|
def test_testcase_loader(self):
|
||||||
testcase_path = "tests/testcases/setup.yml"
|
testcase_path = "tests/testcases/setup.yml"
|
||||||
tests_mapping = loader.load_cases(testcase_path)
|
tests_mapping = loader.load_cases(testcase_path)
|
||||||
@@ -628,7 +592,9 @@ class TestApi(ApiServerUnittest):
|
|||||||
self.assertEqual(testcase_tests[0]["name"], "get token (setup)")
|
self.assertEqual(testcase_tests[0]["name"], "get token (setup)")
|
||||||
self.assertIsInstance(testcase_tests[0]["variables"], dict)
|
self.assertIsInstance(testcase_tests[0]["variables"], dict)
|
||||||
self.assertIn("api_def", testcase_tests[0])
|
self.assertIn("api_def", testcase_tests[0])
|
||||||
self.assertEqual(testcase_tests[0]["api_def"]["request"]["url"], "/api/get-token")
|
self.assertEqual(
|
||||||
|
testcase_tests[0]["api_def"]["request"]["url"], "/api/get-token"
|
||||||
|
)
|
||||||
|
|
||||||
def test_testcase_parser(self):
|
def test_testcase_parser(self):
|
||||||
testcase_path = "tests/testcases/setup.yml"
|
testcase_path = "tests/testcases/setup.yml"
|
||||||
@@ -696,7 +662,7 @@ class TestApi(ApiServerUnittest):
|
|||||||
"tests/testcases/create_user.yml",
|
"tests/testcases/create_user.yml",
|
||||||
"tests/testcases/create_user.v2.yml",
|
"tests/testcases/create_user.v2.yml",
|
||||||
"tests/testcases/create_user.json",
|
"tests/testcases/create_user.json",
|
||||||
"tests/testcases/create_user.v2.json"
|
"tests/testcases/create_user.v2.json",
|
||||||
]:
|
]:
|
||||||
tests_mapping = loader.load_cases(testcase_path)
|
tests_mapping = loader.load_cases(testcase_path)
|
||||||
testcases = parser.parse_tests(tests_mapping)
|
testcases = parser.parse_tests(tests_mapping)
|
||||||
@@ -708,11 +674,10 @@ class TestApi(ApiServerUnittest):
|
|||||||
results = tests_results[0][1]
|
results = tests_results[0][1]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
results.records[0]["name"],
|
results.records[0]["name"],
|
||||||
"setup and reset all (override) for TESTCASE_CREATE_XXX."
|
"setup and reset all (override) for TESTCASE_CREATE_XXX.",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
results.records[1]["name"],
|
results.records[1]["name"], "create user and check result."
|
||||||
"create user and check result."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_testsuite_loader(self):
|
def test_testsuite_loader(self):
|
||||||
@@ -740,11 +705,14 @@ class TestApi(ApiServerUnittest):
|
|||||||
self.assertIn("testcase_def", testcase_tests)
|
self.assertIn("testcase_def", testcase_tests)
|
||||||
self.assertEqual(testcase_tests["name"], "create user 1000 and check result.")
|
self.assertEqual(testcase_tests["name"], "create user 1000 and check result.")
|
||||||
self.assertIsInstance(testcase_tests["testcase_def"], dict)
|
self.assertIsInstance(testcase_tests["testcase_def"], dict)
|
||||||
self.assertEqual(testcase_tests["testcase_def"]["config"]["name"], "create user and check result.")
|
self.assertEqual(
|
||||||
|
testcase_tests["testcase_def"]["config"]["name"],
|
||||||
|
"create user and check result.",
|
||||||
|
)
|
||||||
self.assertEqual(len(testcase_tests["testcase_def"]["teststeps"]), 2)
|
self.assertEqual(len(testcase_tests["testcase_def"]["teststeps"]), 2)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
testcase_tests["testcase_def"]["teststeps"][0]["name"],
|
testcase_tests["testcase_def"]["teststeps"][0]["name"],
|
||||||
"setup and reset all (override) for $device_sn."
|
"setup and reset all (override) for $device_sn.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_testsuite_parser(self):
|
def test_testsuite_parser(self):
|
||||||
@@ -756,14 +724,13 @@ class TestApi(ApiServerUnittest):
|
|||||||
self.assertEqual(len(parsed_testcases[0]["teststeps"]), 2)
|
self.assertEqual(len(parsed_testcases[0]["teststeps"]), 2)
|
||||||
|
|
||||||
testcase1 = parsed_testcases[0]["teststeps"][0]
|
testcase1 = parsed_testcases[0]["teststeps"][0]
|
||||||
self.assertIn("setup and reset all (override)", testcase1["config"]["name"].raw_string)
|
self.assertIn(
|
||||||
|
"setup and reset all (override)", testcase1["config"]["name"].raw_string
|
||||||
|
)
|
||||||
teststeps = testcase1["teststeps"]
|
teststeps = testcase1["teststeps"]
|
||||||
self.assertNotIn("testcase_def", testcase1)
|
self.assertNotIn("testcase_def", testcase1)
|
||||||
self.assertEqual(len(teststeps), 2)
|
self.assertEqual(len(teststeps), 2)
|
||||||
self.assertEqual(
|
self.assertEqual(teststeps[0]["request"]["url"], "/api/get-token")
|
||||||
teststeps[0]["request"]["url"],
|
|
||||||
"/api/get-token"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_testsuite_add_tests(self):
|
def test_testsuite_add_tests(self):
|
||||||
testcase_path = "tests/testsuites/create_users.yml"
|
testcase_path = "tests/testsuites/create_users.yml"
|
||||||
@@ -775,7 +742,9 @@ class TestApi(ApiServerUnittest):
|
|||||||
|
|
||||||
self.assertEqual(len(test_suite._tests), 2)
|
self.assertEqual(len(test_suite._tests), 2)
|
||||||
tests = test_suite._tests[0].teststeps
|
tests = test_suite._tests[0].teststeps
|
||||||
self.assertIn("setup and reset all (override)", tests[0]["config"]["name"].raw_string)
|
self.assertIn(
|
||||||
|
"setup and reset all (override)", tests[0]["config"]["name"].raw_string
|
||||||
|
)
|
||||||
|
|
||||||
def test_testsuite_run_suite(self):
|
def test_testsuite_run_suite(self):
|
||||||
testcase_path = "tests/testsuites/create_users.yml"
|
testcase_path = "tests/testsuites/create_users.yml"
|
||||||
@@ -790,11 +759,5 @@ class TestApi(ApiServerUnittest):
|
|||||||
self.assertEqual(len(tests_results[0][1].records), 2)
|
self.assertEqual(len(tests_results[0][1].records), 2)
|
||||||
|
|
||||||
results = tests_results[0][1]
|
results = tests_results[0][1]
|
||||||
self.assertIn(
|
self.assertIn("setup and reset all (override)", results.records[0]["name"])
|
||||||
"setup and reset all (override)",
|
self.assertEqual(results.records[1]["name"], "create user and check result.")
|
||||||
results.records[0]["name"]
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
results.records[1]["name"],
|
|
||||||
"create user and check result."
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ from tests.base import ApiServerUnittest
|
|||||||
|
|
||||||
|
|
||||||
class TestApiServer(ApiServerUnittest):
|
class TestApiServer(ApiServerUnittest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestApiServer, self).setUp()
|
super(TestApiServer, self).setUp()
|
||||||
self.headers = self.get_authenticated_headers()
|
self.headers = self.get_authenticated_headers()
|
||||||
@@ -25,10 +24,7 @@ class TestApiServer(ApiServerUnittest):
|
|||||||
|
|
||||||
def create_user(self, uid, name, password):
|
def create_user(self, uid, name, password):
|
||||||
url = "%s/api/users/%d" % (self.host, uid)
|
url = "%s/api/users/%d" % (self.host, uid)
|
||||||
data = {
|
data = {"name": name, "password": password}
|
||||||
'name': name,
|
|
||||||
'password': password
|
|
||||||
}
|
|
||||||
return self.api_client.post(url, headers=self.headers, json=data)
|
return self.api_client.post(url, headers=self.headers, json=data)
|
||||||
|
|
||||||
def get_user(self, uid):
|
def get_user(self, uid):
|
||||||
@@ -37,10 +33,7 @@ class TestApiServer(ApiServerUnittest):
|
|||||||
|
|
||||||
def update_user(self, uid, name, password):
|
def update_user(self, uid, name, password):
|
||||||
url = "%s/api/users/%d" % (self.host, uid)
|
url = "%s/api/users/%d" % (self.host, uid)
|
||||||
data = {
|
data = {"name": name, "password": password}
|
||||||
'name': name,
|
|
||||||
'password': password
|
|
||||||
}
|
|
||||||
return self.api_client.put(url, headers=self.headers, json=data)
|
return self.api_client.put(url, headers=self.headers, json=data)
|
||||||
|
|
||||||
def delete_user(self, uid):
|
def delete_user(self, uid):
|
||||||
@@ -50,63 +43,63 @@ class TestApiServer(ApiServerUnittest):
|
|||||||
def test_reset_all(self):
|
def test_reset_all(self):
|
||||||
resp = self.reset_all()
|
resp = self.reset_all()
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(True, resp.json()['success'])
|
self.assertEqual(True, resp.json()["success"])
|
||||||
|
|
||||||
def test_create_user_not_existed(self):
|
def test_create_user_not_existed(self):
|
||||||
resp = self.create_user(1000, 'user1', '123456')
|
resp = self.create_user(1000, "user1", "123456")
|
||||||
self.assertEqual(201, resp.status_code)
|
self.assertEqual(201, resp.status_code)
|
||||||
self.assertEqual(True, resp.json()['success'])
|
self.assertEqual(True, resp.json()["success"])
|
||||||
|
|
||||||
def test_create_user_existed(self):
|
def test_create_user_existed(self):
|
||||||
resp = self.create_user(1000, 'user1', '123456')
|
resp = self.create_user(1000, "user1", "123456")
|
||||||
resp = self.create_user(1000, 'user1', '123456')
|
resp = self.create_user(1000, "user1", "123456")
|
||||||
self.assertEqual(500, resp.status_code)
|
self.assertEqual(500, resp.status_code)
|
||||||
|
|
||||||
def test_get_users_empty(self):
|
def test_get_users_empty(self):
|
||||||
resp = self.get_users()
|
resp = self.get_users()
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(resp.json()['count'], 0)
|
self.assertEqual(resp.json()["count"], 0)
|
||||||
|
|
||||||
def test_get_users_not_empty(self):
|
def test_get_users_not_empty(self):
|
||||||
resp = self.create_user(1000, 'user1', '123456')
|
resp = self.create_user(1000, "user1", "123456")
|
||||||
resp = self.get_users()
|
resp = self.get_users()
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(resp.json()['count'], 1)
|
self.assertEqual(resp.json()["count"], 1)
|
||||||
|
|
||||||
resp = self.create_user(1001, 'user2', '123456')
|
resp = self.create_user(1001, "user2", "123456")
|
||||||
resp = self.get_users()
|
resp = self.get_users()
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(resp.json()['count'], 2)
|
self.assertEqual(resp.json()["count"], 2)
|
||||||
|
|
||||||
def test_get_user_not_existed(self):
|
def test_get_user_not_existed(self):
|
||||||
resp = self.get_user(1000)
|
resp = self.get_user(1000)
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
self.assertEqual(resp.json()['success'], False)
|
self.assertEqual(resp.json()["success"], False)
|
||||||
|
|
||||||
def test_get_user_existed(self):
|
def test_get_user_existed(self):
|
||||||
self.create_user(1000, 'user1', '123456')
|
self.create_user(1000, "user1", "123456")
|
||||||
resp = self.get_user(1000)
|
resp = self.get_user(1000)
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(resp.json()['success'], True)
|
self.assertEqual(resp.json()["success"], True)
|
||||||
|
|
||||||
def test_update_user_not_existed(self):
|
def test_update_user_not_existed(self):
|
||||||
resp = self.update_user(1000, 'user1', '123456')
|
resp = self.update_user(1000, "user1", "123456")
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
self.assertEqual(resp.json()['success'], False)
|
self.assertEqual(resp.json()["success"], False)
|
||||||
|
|
||||||
def test_update_user_existed(self):
|
def test_update_user_existed(self):
|
||||||
self.create_user(1000, 'user1', '123456')
|
self.create_user(1000, "user1", "123456")
|
||||||
resp = self.update_user(1000, 'user2', '123456')
|
resp = self.update_user(1000, "user2", "123456")
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(resp.json()['data']['name'], 'user2')
|
self.assertEqual(resp.json()["data"]["name"], "user2")
|
||||||
|
|
||||||
def test_delete_user_not_existed(self):
|
def test_delete_user_not_existed(self):
|
||||||
resp = self.delete_user(1000)
|
resp = self.delete_user(1000)
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
self.assertEqual(resp.json()['success'], False)
|
self.assertEqual(resp.json()["success"], False)
|
||||||
|
|
||||||
def test_delete_user_existed(self):
|
def test_delete_user_existed(self):
|
||||||
self.create_user(1000, 'leo', '123456')
|
self.create_user(1000, "leo", "123456")
|
||||||
resp = self.delete_user(1000)
|
resp = self.delete_user(1000)
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(resp.json()['success'], True)
|
self.assertEqual(resp.json()["success"], True)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from httprunner.compat import io
|
|||||||
|
|
||||||
|
|
||||||
class TestCli(unittest.TestCase):
|
class TestCli(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.captured_output = io.StringIO()
|
self.captured_output = io.StringIO()
|
||||||
sys.stdout = self.captured_output
|
sys.stdout = self.captured_output
|
||||||
@@ -23,6 +22,7 @@ class TestCli(unittest.TestCase):
|
|||||||
self.assertEqual(cm.exception.code, 0)
|
self.assertEqual(cm.exception.code, 0)
|
||||||
|
|
||||||
from httprunner import __version__
|
from httprunner import __version__
|
||||||
|
|
||||||
self.assertIn(__version__, self.captured_output.getvalue().strip())
|
self.assertIn(__version__, self.captured_output.getvalue().strip())
|
||||||
|
|
||||||
def test_show_help(self):
|
def test_show_help(self):
|
||||||
@@ -34,4 +34,5 @@ class TestCli(unittest.TestCase):
|
|||||||
self.assertEqual(cm.exception.code, 0)
|
self.assertEqual(cm.exception.code, 0)
|
||||||
|
|
||||||
from httprunner import __description__
|
from httprunner import __description__
|
||||||
|
|
||||||
self.assertIn(__description__, self.captured_output.getvalue().strip())
|
self.assertIn(__description__, self.captured_output.getvalue().strip())
|
||||||
|
|||||||
@@ -20,30 +20,21 @@ class TestHttpClient(ApiServerUnittest):
|
|||||||
|
|
||||||
def test_request_with_full_url(self):
|
def test_request_with_full_url(self):
|
||||||
url = "%s/api/users/1000" % self.host
|
url = "%s/api/users/1000" % self.host
|
||||||
data = {
|
data = {"name": "user1", "password": "123456"}
|
||||||
'name': 'user1',
|
|
||||||
'password': '123456'
|
|
||||||
}
|
|
||||||
resp = self.api_client.post(url, json=data, headers=self.headers)
|
resp = self.api_client.post(url, json=data, headers=self.headers)
|
||||||
self.assertEqual(201, resp.status_code)
|
self.assertEqual(201, resp.status_code)
|
||||||
self.assertEqual(True, resp.json()['success'])
|
self.assertEqual(True, resp.json()["success"])
|
||||||
|
|
||||||
def test_request_without_base_url(self):
|
def test_request_without_base_url(self):
|
||||||
url = "{}/api/users/1000".format(self.host)
|
url = "{}/api/users/1000".format(self.host)
|
||||||
data = {
|
data = {"name": "user1", "password": "123456"}
|
||||||
'name': 'user1',
|
|
||||||
'password': '123456'
|
|
||||||
}
|
|
||||||
resp = self.api_client.post(url, json=data, headers=self.headers)
|
resp = self.api_client.post(url, json=data, headers=self.headers)
|
||||||
self.assertEqual(201, resp.status_code)
|
self.assertEqual(201, resp.status_code)
|
||||||
self.assertEqual(True, resp.json()['success'])
|
self.assertEqual(True, resp.json()["success"])
|
||||||
|
|
||||||
def test_request_post_data(self):
|
def test_request_post_data(self):
|
||||||
url = "{}/api/users/1000".format(self.host)
|
url = "{}/api/users/1000".format(self.host)
|
||||||
data = {
|
data = {"name": "user1", "password": "123456"}
|
||||||
'name': 'user1',
|
|
||||||
'password': '123456'
|
|
||||||
}
|
|
||||||
resp = self.api_client.post(url, json=data, headers=self.headers)
|
resp = self.api_client.post(url, json=data, headers=self.headers)
|
||||||
# b'{"name": "user1", "password": "123456"}'
|
# b'{"name": "user1", "password": "123456"}'
|
||||||
self.assertIn(b'"name": "user1"', resp.request.body)
|
self.assertIn(b'"name": "user1"', resp.request.body)
|
||||||
@@ -56,14 +47,8 @@ class TestHttpClient(ApiServerUnittest):
|
|||||||
|
|
||||||
def test_request_with_cookies(self):
|
def test_request_with_cookies(self):
|
||||||
url = "{}/api/users/1000".format(self.host)
|
url = "{}/api/users/1000".format(self.host)
|
||||||
data = {
|
data = {"name": "user1", "password": "123456"}
|
||||||
'name': 'user1',
|
cookies = {"a": "1", "b": "2"}
|
||||||
'password': '123456'
|
|
||||||
}
|
|
||||||
cookies = {
|
|
||||||
"a": "1",
|
|
||||||
"b": "2"
|
|
||||||
}
|
|
||||||
resp = self.api_client.get(url, cookies=cookies, headers=self.headers)
|
resp = self.api_client.get(url, cookies=cookies, headers=self.headers)
|
||||||
self.assertEqual(resp.request._cookies["a"], "1")
|
self.assertEqual(resp.request._cookies["a"], "1")
|
||||||
self.assertEqual(resp.request._cookies["b"], "2")
|
self.assertEqual(resp.request._cookies["b"], "2")
|
||||||
|
|||||||
@@ -6,17 +6,13 @@ from tests.base import ApiServerUnittest, gen_random_string
|
|||||||
|
|
||||||
|
|
||||||
class TestContext(ApiServerUnittest):
|
class TestContext(ApiServerUnittest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
loader.load_project_data(os.path.join(os.getcwd(), "tests"))
|
loader.load_project_data(os.path.join(os.getcwd(), "tests"))
|
||||||
self.context = context.SessionContext(
|
self.context = context.SessionContext(variables={"SECRET_KEY": "DebugTalk"})
|
||||||
variables={"SECRET_KEY": "DebugTalk"}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_init_test_variables_initialize(self):
|
def test_init_test_variables_initialize(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.context.test_variables_mapping,
|
self.context.test_variables_mapping, {"SECRET_KEY": "DebugTalk"}
|
||||||
{'SECRET_KEY': 'DebugTalk'}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_init_test_variables(self):
|
def test_init_test_variables(self):
|
||||||
@@ -28,36 +24,25 @@ class TestContext(ApiServerUnittest):
|
|||||||
# "data": '{"name": "$username", "password": "123456"}',
|
# "data": '{"name": "$username", "password": "123456"}',
|
||||||
"TOKEN": "debugtalk",
|
"TOKEN": "debugtalk",
|
||||||
"username": "user1",
|
"username": "user1",
|
||||||
"num": 6
|
"num": 6,
|
||||||
}
|
|
||||||
functions = {
|
|
||||||
"gen_random_string": gen_random_string,
|
|
||||||
"gen_md5": gen_md5
|
|
||||||
}
|
}
|
||||||
|
functions = {"gen_random_string": gen_random_string, "gen_md5": gen_md5}
|
||||||
variables = parser.prepare_lazy_data(variables, functions, variables.keys())
|
variables = parser.prepare_lazy_data(variables, functions, variables.keys())
|
||||||
variables = parser.parse_variables_mapping(variables)
|
variables = parser.parse_variables_mapping(variables)
|
||||||
self.context.init_test_variables(variables)
|
self.context.init_test_variables(variables)
|
||||||
variables_mapping = self.context.test_variables_mapping
|
variables_mapping = self.context.test_variables_mapping
|
||||||
self.assertEqual(len(variables_mapping["random"]), 6)
|
self.assertEqual(len(variables_mapping["random"]), 6)
|
||||||
self.assertEqual(len(variables_mapping["authorization"]), 32)
|
self.assertEqual(len(variables_mapping["authorization"]), 32)
|
||||||
self.assertEqual(variables_mapping["data"], 'user1')
|
self.assertEqual(variables_mapping["data"], "user1")
|
||||||
|
|
||||||
def test_update_seesion_variables(self):
|
def test_update_seesion_variables(self):
|
||||||
self.context.update_session_variables({"TOKEN": "debugtalk"})
|
self.context.update_session_variables({"TOKEN": "debugtalk"})
|
||||||
self.assertEqual(
|
self.assertEqual(self.context.session_variables_mapping["TOKEN"], "debugtalk")
|
||||||
self.context.session_variables_mapping["TOKEN"],
|
|
||||||
"debugtalk"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_eval_content_variables(self):
|
def test_eval_content_variables(self):
|
||||||
variables = {
|
variables = {"SECRET_KEY": "DebugTalk"}
|
||||||
"SECRET_KEY": "DebugTalk"
|
|
||||||
}
|
|
||||||
content = parser.prepare_lazy_data("abc$SECRET_KEY", {}, variables.keys())
|
content = parser.prepare_lazy_data("abc$SECRET_KEY", {}, variables.keys())
|
||||||
self.assertEqual(
|
self.assertEqual(self.context.eval_content(content), "abcDebugTalk")
|
||||||
self.context.eval_content(content),
|
|
||||||
"abcDebugTalk"
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: fix variable extraction
|
# TODO: fix variable extraction
|
||||||
# content = "abc$SECRET_KEYdef"
|
# content = "abc$SECRET_KEYdef"
|
||||||
@@ -71,12 +56,9 @@ class TestContext(ApiServerUnittest):
|
|||||||
"random": "${gen_random_string(5)}",
|
"random": "${gen_random_string(5)}",
|
||||||
"data": '{"name": "user", "password": "123456"}',
|
"data": '{"name": "user", "password": "123456"}',
|
||||||
"authorization": "${gen_md5($TOKEN, $data, $random)}",
|
"authorization": "${gen_md5($TOKEN, $data, $random)}",
|
||||||
"TOKEN": "debugtalk"
|
"TOKEN": "debugtalk",
|
||||||
}
|
|
||||||
functions = {
|
|
||||||
"gen_random_string": gen_random_string,
|
|
||||||
"gen_md5": gen_md5
|
|
||||||
}
|
}
|
||||||
|
functions = {"gen_random_string": gen_random_string, "gen_md5": gen_md5}
|
||||||
variables = parser.prepare_lazy_data(variables, functions, variables.keys())
|
variables = parser.prepare_lazy_data(variables, functions, variables.keys())
|
||||||
variables = parser.parse_variables_mapping(variables)
|
variables = parser.parse_variables_mapping(variables)
|
||||||
self.context.init_test_variables(variables)
|
self.context.init_test_variables(variables)
|
||||||
@@ -88,14 +70,12 @@ class TestContext(ApiServerUnittest):
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"authorization": "$authorization",
|
"authorization": "$authorization",
|
||||||
"random": "$random",
|
"random": "$random",
|
||||||
"secret_key": "$SECRET_KEY"
|
"secret_key": "$SECRET_KEY",
|
||||||
},
|
},
|
||||||
"data": "$data"
|
"data": "$data",
|
||||||
}
|
}
|
||||||
prepared_request = parser.prepare_lazy_data(
|
prepared_request = parser.prepare_lazy_data(
|
||||||
request,
|
request, functions, {"authorization", "random", "SECRET_KEY", "data"}
|
||||||
functions,
|
|
||||||
{"authorization", "random", "SECRET_KEY", "data"}
|
|
||||||
)
|
)
|
||||||
parsed_request = self.context.eval_content(prepared_request)
|
parsed_request = self.context.eval_content(prepared_request)
|
||||||
self.assertIn("authorization", parsed_request["headers"])
|
self.assertIn("authorization", parsed_request["headers"])
|
||||||
@@ -104,17 +84,14 @@ class TestContext(ApiServerUnittest):
|
|||||||
self.assertEqual(len(parsed_request["headers"]["random"]), 5)
|
self.assertEqual(len(parsed_request["headers"]["random"]), 5)
|
||||||
self.assertIn("data", parsed_request)
|
self.assertIn("data", parsed_request)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parsed_request["data"],
|
parsed_request["data"], '{"name": "user", "password": "123456"}'
|
||||||
'{"name": "user", "password": "123456"}'
|
|
||||||
)
|
)
|
||||||
self.assertEqual(parsed_request["headers"]["secret_key"], "DebugTalk")
|
self.assertEqual(parsed_request["headers"]["secret_key"], "DebugTalk")
|
||||||
|
|
||||||
def test_validate(self):
|
def test_validate(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "test validation"},
|
||||||
'name': "test validation"
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "test validation",
|
"name": "test validation",
|
||||||
@@ -124,26 +101,32 @@ class TestContext(ApiServerUnittest):
|
|||||||
},
|
},
|
||||||
"variables": {
|
"variables": {
|
||||||
"resp_status_code": 200,
|
"resp_status_code": 200,
|
||||||
"resp_body_success": True
|
"resp_body_success": True,
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [
|
||||||
{"eq": ["$resp_status_code", 200]},
|
{"eq": ["$resp_status_code", 200]},
|
||||||
{"check": "$resp_status_code", "comparator": "eq", "expect": 200},
|
{
|
||||||
|
"check": "$resp_status_code",
|
||||||
|
"comparator": "eq",
|
||||||
|
"expect": 200,
|
||||||
|
},
|
||||||
{"check": "$resp_body_success", "expect": True},
|
{"check": "$resp_body_success", "expect": True},
|
||||||
{"check": "${is_status_code_200($resp_status_code)}", "expect": True}
|
{
|
||||||
]
|
"check": "${is_status_code_200($resp_status_code)}",
|
||||||
|
"expect": True,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
from tests.debugtalk import is_status_code_200
|
from tests.debugtalk import is_status_code_200
|
||||||
|
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {
|
||||||
"functions": {
|
"functions": {"is_status_code_200": is_status_code_200}
|
||||||
"is_status_code_200": is_status_code_200
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"testcases": testcases
|
"testcases": testcases,
|
||||||
}
|
}
|
||||||
testcases = parser.parse_tests(tests_mapping)
|
testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = testcases[0]
|
parsed_testcase = testcases[0]
|
||||||
@@ -154,9 +137,7 @@ class TestContext(ApiServerUnittest):
|
|||||||
def test_validate_exception(self):
|
def test_validate_exception(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "test validation"},
|
||||||
'name': "test validation"
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "test validation",
|
"name": "test validation",
|
||||||
@@ -166,20 +147,22 @@ class TestContext(ApiServerUnittest):
|
|||||||
},
|
},
|
||||||
"variables": {
|
"variables": {
|
||||||
"resp_status_code": 200,
|
"resp_status_code": 200,
|
||||||
"resp_body_success": True
|
"resp_body_success": True,
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [
|
||||||
{"eq": ["$resp_status_code", 201]},
|
{"eq": ["$resp_status_code", 201]},
|
||||||
{"check": "$resp_status_code", "expect": 201},
|
{"check": "$resp_status_code", "expect": 201},
|
||||||
{"check": "$resp_body_success", "comparator": "eq", "expect": True}
|
{
|
||||||
]
|
"check": "$resp_body_success",
|
||||||
|
"comparator": "eq",
|
||||||
|
"expect": True,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {"testcases": testcases}
|
||||||
"testcases": testcases
|
|
||||||
}
|
|
||||||
testcases = parser.parse_tests(tests_mapping)
|
testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = testcases[0]
|
parsed_testcase = testcases[0]
|
||||||
test_runner = runner.Runner(parsed_testcase["config"])
|
test_runner = runner.Runner(parsed_testcase["config"])
|
||||||
|
|||||||
@@ -5,15 +5,13 @@ from httprunner.ext.locusts.utils import prepare_locust_tests
|
|||||||
|
|
||||||
|
|
||||||
class TestLocust(unittest.TestCase):
|
class TestLocust(unittest.TestCase):
|
||||||
|
|
||||||
def test_prepare_locust_tests(self):
|
def test_prepare_locust_tests(self):
|
||||||
path = os.path.join(
|
path = os.path.join(os.getcwd(), "tests/locust_tests/demo_locusts.yml")
|
||||||
os.getcwd(), 'tests/locust_tests/demo_locusts.yml')
|
|
||||||
locust_tests = prepare_locust_tests(path)
|
locust_tests = prepare_locust_tests(path)
|
||||||
self.assertEqual(len(locust_tests), 2 + 3)
|
self.assertEqual(len(locust_tests), 2 + 3)
|
||||||
name_list = [
|
name_list = [
|
||||||
"create user 1000 and check result.",
|
"create user 1000 and check result.",
|
||||||
"create user 1001 and check result."
|
"create user 1001 and check result.",
|
||||||
]
|
]
|
||||||
self.assertIn(locust_tests[0]["config"]["name"], name_list)
|
self.assertIn(locust_tests[0]["config"]["name"], name_list)
|
||||||
self.assertIn(locust_tests[4]["config"]["name"], name_list)
|
self.assertIn(locust_tests[4]["config"]["name"], name_list)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@@ -7,14 +6,15 @@ from httprunner.loader import buildup
|
|||||||
|
|
||||||
|
|
||||||
class TestModuleLoader(unittest.TestCase):
|
class TestModuleLoader(unittest.TestCase):
|
||||||
|
|
||||||
def test_filter_module_functions(self):
|
def test_filter_module_functions(self):
|
||||||
module_functions = buildup.load_module_functions(buildup)
|
module_functions = buildup.load_module_functions(buildup)
|
||||||
self.assertIn("load_module_functions", module_functions)
|
self.assertIn("load_module_functions", module_functions)
|
||||||
self.assertNotIn("is_py3", module_functions)
|
self.assertNotIn("is_py3", module_functions)
|
||||||
|
|
||||||
def test_load_debugtalk_module(self):
|
def test_load_debugtalk_module(self):
|
||||||
project_mapping = buildup.load_project_data(os.path.join(os.getcwd(), "httprunner"))
|
project_mapping = buildup.load_project_data(
|
||||||
|
os.path.join(os.getcwd(), "httprunner")
|
||||||
|
)
|
||||||
self.assertNotIn("alter_response", project_mapping["functions"])
|
self.assertNotIn("alter_response", project_mapping["functions"])
|
||||||
|
|
||||||
project_mapping = buildup.load_project_data(os.path.join(os.getcwd(), "tests"))
|
project_mapping = buildup.load_project_data(os.path.join(os.getcwd(), "tests"))
|
||||||
@@ -28,51 +28,38 @@ class TestModuleLoader(unittest.TestCase):
|
|||||||
project_mapping = buildup.load_project_data("tests/data/demo_testcase.yml")
|
project_mapping = buildup.load_project_data("tests/data/demo_testcase.yml")
|
||||||
project_working_directory = project_mapping["PWD"]
|
project_working_directory = project_mapping["PWD"]
|
||||||
debugtalk_functions = project_mapping["functions"]
|
debugtalk_functions = project_mapping["functions"]
|
||||||
self.assertEqual(
|
self.assertEqual(project_working_directory, os.path.join(os.getcwd(), "tests"))
|
||||||
project_working_directory,
|
|
||||||
os.path.join(os.getcwd(), "tests")
|
|
||||||
)
|
|
||||||
self.assertIn("gen_md5", debugtalk_functions)
|
self.assertIn("gen_md5", debugtalk_functions)
|
||||||
|
|
||||||
project_mapping = buildup.load_project_data("tests/base.py")
|
project_mapping = buildup.load_project_data("tests/base.py")
|
||||||
project_working_directory = project_mapping["PWD"]
|
project_working_directory = project_mapping["PWD"]
|
||||||
debugtalk_functions = project_mapping["functions"]
|
debugtalk_functions = project_mapping["functions"]
|
||||||
self.assertEqual(
|
self.assertEqual(project_working_directory, os.path.join(os.getcwd(), "tests"))
|
||||||
project_working_directory,
|
|
||||||
os.path.join(os.getcwd(), "tests")
|
|
||||||
)
|
|
||||||
self.assertIn("gen_md5", debugtalk_functions)
|
self.assertIn("gen_md5", debugtalk_functions)
|
||||||
|
|
||||||
project_mapping = buildup.load_project_data("httprunner/__init__.py")
|
project_mapping = buildup.load_project_data("httprunner/__init__.py")
|
||||||
project_working_directory = project_mapping["PWD"]
|
project_working_directory = project_mapping["PWD"]
|
||||||
debugtalk_functions = project_mapping["functions"]
|
debugtalk_functions = project_mapping["functions"]
|
||||||
self.assertEqual(
|
self.assertEqual(project_working_directory, os.getcwd())
|
||||||
project_working_directory,
|
|
||||||
os.getcwd()
|
|
||||||
)
|
|
||||||
self.assertEqual(debugtalk_functions, {})
|
self.assertEqual(debugtalk_functions, {})
|
||||||
|
|
||||||
|
|
||||||
class TestSuiteLoader(unittest.TestCase):
|
class TestSuiteLoader(unittest.TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.project_mapping = buildup.load_project_data(os.path.join(os.getcwd(), "tests"))
|
cls.project_mapping = buildup.load_project_data(
|
||||||
|
os.path.join(os.getcwd(), "tests")
|
||||||
|
)
|
||||||
cls.tests_def_mapping = buildup.tests_def_mapping
|
cls.tests_def_mapping = buildup.tests_def_mapping
|
||||||
|
|
||||||
def test_load_teststep_api(self):
|
def test_load_teststep_api(self):
|
||||||
raw_test = {
|
raw_test = {
|
||||||
"name": "create user (override).",
|
"name": "create user (override).",
|
||||||
"api": "api/create_user.yml",
|
"api": "api/create_user.yml",
|
||||||
"variables": [
|
"variables": [{"uid": "999"}],
|
||||||
{"uid": "999"}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
teststep = buildup.load_teststep(raw_test)
|
teststep = buildup.load_teststep(raw_test)
|
||||||
self.assertEqual(
|
self.assertEqual("create user (override).", teststep["name"])
|
||||||
"create user (override).",
|
|
||||||
teststep["name"]
|
|
||||||
)
|
|
||||||
self.assertIn("api_def", teststep)
|
self.assertIn("api_def", teststep)
|
||||||
api_def = teststep["api_def"]
|
api_def = teststep["api_def"]
|
||||||
self.assertEqual(api_def["name"], "create user")
|
self.assertEqual(api_def["name"], "create user")
|
||||||
@@ -82,15 +69,10 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
raw_test = {
|
raw_test = {
|
||||||
"name": "setup and reset all (override).",
|
"name": "setup and reset all (override).",
|
||||||
"testcase": "testcases/setup.yml",
|
"testcase": "testcases/setup.yml",
|
||||||
"variables": [
|
"variables": [{"device_sn": "$device_sn"}],
|
||||||
{"device_sn": "$device_sn"}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
testcase = buildup.load_teststep(raw_test)
|
testcase = buildup.load_teststep(raw_test)
|
||||||
self.assertEqual(
|
self.assertEqual("setup and reset all (override).", testcase["name"])
|
||||||
"setup and reset all (override).",
|
|
||||||
testcase["name"]
|
|
||||||
)
|
|
||||||
tests = testcase["testcase_def"]["teststeps"]
|
tests = testcase["testcase_def"]["teststeps"]
|
||||||
self.assertEqual(len(tests), 2)
|
self.assertEqual(len(tests), 2)
|
||||||
self.assertEqual(tests[0]["name"], "get token (setup)")
|
self.assertEqual(tests[0]["name"], "get token (setup)")
|
||||||
@@ -106,7 +88,7 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
def test_load_test_file_testcase(self):
|
def test_load_test_file_testcase(self):
|
||||||
for loaded_content in [
|
for loaded_content in [
|
||||||
buildup.load_test_file("tests/testcases/setup.yml"),
|
buildup.load_test_file("tests/testcases/setup.yml"),
|
||||||
buildup.load_test_file("tests/testcases/setup.json")
|
buildup.load_test_file("tests/testcases/setup.json"),
|
||||||
]:
|
]:
|
||||||
self.assertEqual(loaded_content["type"], "testcase")
|
self.assertEqual(loaded_content["type"], "testcase")
|
||||||
self.assertIn("path", loaded_content)
|
self.assertIn("path", loaded_content)
|
||||||
@@ -118,7 +100,7 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
def test_load_test_file_testcase_v2(self):
|
def test_load_test_file_testcase_v2(self):
|
||||||
for loaded_content in [
|
for loaded_content in [
|
||||||
buildup.load_test_file("tests/testcases/setup.v2.yml"),
|
buildup.load_test_file("tests/testcases/setup.v2.yml"),
|
||||||
buildup.load_test_file("tests/testcases/setup.v2.json")
|
buildup.load_test_file("tests/testcases/setup.v2.json"),
|
||||||
]:
|
]:
|
||||||
self.assertEqual(loaded_content["type"], "testcase")
|
self.assertEqual(loaded_content["type"], "testcase")
|
||||||
self.assertIn("path", loaded_content)
|
self.assertIn("path", loaded_content)
|
||||||
@@ -130,38 +112,45 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
def test_load_test_file_testsuite(self):
|
def test_load_test_file_testsuite(self):
|
||||||
for loaded_content in [
|
for loaded_content in [
|
||||||
buildup.load_test_file("tests/testsuites/create_users.yml"),
|
buildup.load_test_file("tests/testsuites/create_users.yml"),
|
||||||
buildup.load_test_file("tests/testsuites/create_users.json")
|
buildup.load_test_file("tests/testsuites/create_users.json"),
|
||||||
]:
|
]:
|
||||||
self.assertEqual(loaded_content["type"], "testsuite")
|
self.assertEqual(loaded_content["type"], "testsuite")
|
||||||
|
|
||||||
testcases = loaded_content["testcases"]
|
testcases = loaded_content["testcases"]
|
||||||
self.assertEqual(len(testcases), 2)
|
self.assertEqual(len(testcases), 2)
|
||||||
self.assertIn('create user 1000 and check result.', testcases)
|
self.assertIn("create user 1000 and check result.", testcases)
|
||||||
self.assertIn('testcase_def', testcases["create user 1000 and check result."])
|
self.assertIn(
|
||||||
|
"testcase_def", testcases["create user 1000 and check result."]
|
||||||
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
testcases["create user 1000 and check result."]["testcase_def"]["config"]["name"],
|
testcases["create user 1000 and check result."]["testcase_def"][
|
||||||
"create user and check result."
|
"config"
|
||||||
|
]["name"],
|
||||||
|
"create user and check result.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_load_test_file_testsuite_v2(self):
|
def test_load_test_file_testsuite_v2(self):
|
||||||
for loaded_content in [
|
for loaded_content in [
|
||||||
buildup.load_test_file("tests/testsuites/create_users.v2.yml"),
|
buildup.load_test_file("tests/testsuites/create_users.v2.yml"),
|
||||||
buildup.load_test_file("tests/testsuites/create_users.v2.json")
|
buildup.load_test_file("tests/testsuites/create_users.v2.json"),
|
||||||
]:
|
]:
|
||||||
self.assertEqual(loaded_content["type"], "testsuite")
|
self.assertEqual(loaded_content["type"], "testsuite")
|
||||||
|
|
||||||
testcases = loaded_content["testcases"]
|
testcases = loaded_content["testcases"]
|
||||||
self.assertEqual(len(testcases), 2)
|
self.assertEqual(len(testcases), 2)
|
||||||
self.assertIn('create user 1000 and check result.', testcases)
|
self.assertIn("create user 1000 and check result.", testcases)
|
||||||
self.assertIn('testcase_def', testcases["create user 1000 and check result."])
|
self.assertIn(
|
||||||
|
"testcase_def", testcases["create user 1000 and check result."]
|
||||||
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
testcases["create user 1000 and check result."]["testcase_def"]["config"]["name"],
|
testcases["create user 1000 and check result."]["testcase_def"][
|
||||||
"create user and check result."
|
"config"
|
||||||
|
]["name"],
|
||||||
|
"create user and check result.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_load_tests_api_file(self):
|
def test_load_tests_api_file(self):
|
||||||
path = os.path.join(
|
path = os.path.join(os.getcwd(), "tests/api/create_user.yml")
|
||||||
os.getcwd(), 'tests/api/create_user.yml')
|
|
||||||
tests_mapping = loader.load_cases(path)
|
tests_mapping = loader.load_cases(path)
|
||||||
project_mapping = tests_mapping["project_mapping"]
|
project_mapping = tests_mapping["project_mapping"]
|
||||||
api_list = tests_mapping["apis"]
|
api_list = tests_mapping["apis"]
|
||||||
@@ -170,8 +159,7 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
|
|
||||||
def test_load_tests_testcase_file(self):
|
def test_load_tests_testcase_file(self):
|
||||||
# absolute file path
|
# absolute file path
|
||||||
path = os.path.join(
|
path = os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.json")
|
||||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.json')
|
|
||||||
tests_mapping = loader.load_cases(path)
|
tests_mapping = loader.load_cases(path)
|
||||||
project_mapping = tests_mapping["project_mapping"]
|
project_mapping = tests_mapping["project_mapping"]
|
||||||
testcases_list = tests_mapping["testcases"]
|
testcases_list = tests_mapping["testcases"]
|
||||||
@@ -180,7 +168,7 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
self.assertIn("get_sign", project_mapping["functions"])
|
self.assertIn("get_sign", project_mapping["functions"])
|
||||||
|
|
||||||
# relative file path
|
# relative file path
|
||||||
path = 'tests/data/demo_testcase_hardcode.yml'
|
path = "tests/data/demo_testcase_hardcode.yml"
|
||||||
tests_mapping = loader.load_cases(path)
|
tests_mapping = loader.load_cases(path)
|
||||||
project_mapping = tests_mapping["project_mapping"]
|
project_mapping = tests_mapping["project_mapping"]
|
||||||
testcases_list = tests_mapping["testcases"]
|
testcases_list = tests_mapping["testcases"]
|
||||||
@@ -189,91 +177,75 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
self.assertIn("get_sign", project_mapping["functions"])
|
self.assertIn("get_sign", project_mapping["functions"])
|
||||||
|
|
||||||
def test_load_tests_testcase_file_2(self):
|
def test_load_tests_testcase_file_2(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(os.getcwd(), "tests/data/demo_testcase.yml")
|
||||||
os.getcwd(), 'tests/data/demo_testcase.yml')
|
|
||||||
tests_mapping = loader.load_cases(testcase_file_path)
|
tests_mapping = loader.load_cases(testcase_file_path)
|
||||||
testcases = tests_mapping["testcases"]
|
testcases = tests_mapping["testcases"]
|
||||||
self.assertIsInstance(testcases, list)
|
self.assertIsInstance(testcases, list)
|
||||||
self.assertEqual(testcases[0]["config"]["name"], '123t$var_a')
|
self.assertEqual(testcases[0]["config"]["name"], "123t$var_a")
|
||||||
self.assertIn(
|
self.assertIn("sum_two", tests_mapping["project_mapping"]["functions"])
|
||||||
"sum_two",
|
self.assertEqual(
|
||||||
tests_mapping["project_mapping"]["functions"]
|
testcases[0]["config"]["variables"]["var_c"], "${sum_two($var_a, $var_b)}"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
testcases[0]["config"]["variables"]["var_c"],
|
testcases[0]["config"]["variables"]["PROJECT_KEY"], "${ENV(PROJECT_KEY)}"
|
||||||
"${sum_two($var_a, $var_b)}"
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
testcases[0]["config"]["variables"]["PROJECT_KEY"],
|
|
||||||
"${ENV(PROJECT_KEY)}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_load_tests_testcase_file_with_api_ref(self):
|
def test_load_tests_testcase_file_with_api_ref(self):
|
||||||
path = os.path.join(
|
path = os.path.join(os.getcwd(), "tests/data/demo_testcase_layer.yml")
|
||||||
os.getcwd(), 'tests/data/demo_testcase_layer.yml')
|
|
||||||
tests_mapping = loader.load_cases(path)
|
tests_mapping = loader.load_cases(path)
|
||||||
project_mapping = tests_mapping["project_mapping"]
|
project_mapping = tests_mapping["project_mapping"]
|
||||||
testcases_list = tests_mapping["testcases"]
|
testcases_list = tests_mapping["testcases"]
|
||||||
self.assertIn('device_sn', testcases_list[0]["config"]["variables"])
|
self.assertIn("device_sn", testcases_list[0]["config"]["variables"])
|
||||||
self.assertIn("gen_md5", project_mapping["functions"])
|
self.assertIn("gen_md5", project_mapping["functions"])
|
||||||
self.assertIn("base_url", testcases_list[0]["config"])
|
self.assertIn("base_url", testcases_list[0]["config"])
|
||||||
test_dict0 = testcases_list[0]["teststeps"][0]
|
test_dict0 = testcases_list[0]["teststeps"][0]
|
||||||
self.assertEqual(
|
self.assertEqual("get token with $user_agent, $app_version", test_dict0["name"])
|
||||||
"get token with $user_agent, $app_version",
|
|
||||||
test_dict0["name"]
|
|
||||||
)
|
|
||||||
self.assertIn("/api/get-token", test_dict0["api_def"]["request"]["url"])
|
self.assertIn("/api/get-token", test_dict0["api_def"]["request"]["url"])
|
||||||
self.assertIn(
|
self.assertIn({"eq": ["status_code", 200]}, test_dict0["validate"])
|
||||||
{'eq': ['status_code', 200]},
|
|
||||||
test_dict0["validate"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_load_tests_testsuite_file_with_testcase_ref(self):
|
def test_load_tests_testsuite_file_with_testcase_ref(self):
|
||||||
path = os.path.join(
|
path = os.path.join(os.getcwd(), "tests/testsuites/create_users.yml")
|
||||||
os.getcwd(), 'tests/testsuites/create_users.yml')
|
|
||||||
tests_mapping = loader.load_cases(path)
|
tests_mapping = loader.load_cases(path)
|
||||||
project_mapping = tests_mapping["project_mapping"]
|
project_mapping = tests_mapping["project_mapping"]
|
||||||
testsuites_list = tests_mapping["testsuites"]
|
testsuites_list = tests_mapping["testsuites"]
|
||||||
|
|
||||||
|
self.assertEqual("create users with uid", testsuites_list[0]["config"]["name"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"create users with uid",
|
"${gen_random_string(15)}",
|
||||||
testsuites_list[0]["config"]["name"]
|
testsuites_list[0]["config"]["variables"]["device_sn"],
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
'${gen_random_string(15)}',
|
|
||||||
testsuites_list[0]["config"]["variables"]['device_sn']
|
|
||||||
)
|
)
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"create user 1000 and check result.",
|
"create user 1000 and check result.", testsuites_list[0]["testcases"]
|
||||||
testsuites_list[0]["testcases"]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
testsuites_list[0]["testcases"]["create user 1000 and check result."]["testcase_def"]["config"]["name"],
|
testsuites_list[0]["testcases"]["create user 1000 and check result."][
|
||||||
"create user and check result."
|
"testcase_def"
|
||||||
|
]["config"]["name"],
|
||||||
|
"create user and check result.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_load_tests_folder_path(self):
|
def test_load_tests_folder_path(self):
|
||||||
# absolute folder path
|
# absolute folder path
|
||||||
path = os.path.join(os.getcwd(), 'tests/data')
|
path = os.path.join(os.getcwd(), "tests/data")
|
||||||
tests_mapping = loader.load_cases(path)
|
tests_mapping = loader.load_cases(path)
|
||||||
testcase_list_1 = tests_mapping["testcases"]
|
testcase_list_1 = tests_mapping["testcases"]
|
||||||
self.assertGreater(len(testcase_list_1), 4)
|
self.assertGreater(len(testcase_list_1), 4)
|
||||||
|
|
||||||
# relative folder path
|
# relative folder path
|
||||||
path = 'tests/data/'
|
path = "tests/data/"
|
||||||
tests_mapping = loader.load_cases(path)
|
tests_mapping = loader.load_cases(path)
|
||||||
testcase_list_2 = tests_mapping["testcases"]
|
testcase_list_2 = tests_mapping["testcases"]
|
||||||
self.assertEqual(len(testcase_list_1), len(testcase_list_2))
|
self.assertEqual(len(testcase_list_1), len(testcase_list_2))
|
||||||
|
|
||||||
def test_load_tests_path_not_exist(self):
|
def test_load_tests_path_not_exist(self):
|
||||||
# absolute folder path
|
# absolute folder path
|
||||||
path = os.path.join(os.getcwd(), 'tests/data_not_exist')
|
path = os.path.join(os.getcwd(), "tests/data_not_exist")
|
||||||
with self.assertRaises(exceptions.FileNotFound):
|
with self.assertRaises(exceptions.FileNotFound):
|
||||||
loader.load_cases(path)
|
loader.load_cases(path)
|
||||||
|
|
||||||
# relative folder path
|
# relative folder path
|
||||||
path = 'tests/data_not_exist'
|
path = "tests/data_not_exist"
|
||||||
with self.assertRaises(exceptions.FileNotFound):
|
with self.assertRaises(exceptions.FileNotFound):
|
||||||
loader.load_cases(path)
|
loader.load_cases(path)
|
||||||
|
|
||||||
@@ -281,5 +253,11 @@ class TestSuiteLoader(unittest.TestCase):
|
|||||||
buildup.load_project_data(os.path.join(os.getcwd(), "tests"))
|
buildup.load_project_data(os.path.join(os.getcwd(), "tests"))
|
||||||
self.assertIn("gen_md5", self.project_mapping["functions"])
|
self.assertIn("gen_md5", self.project_mapping["functions"])
|
||||||
self.assertEqual(self.project_mapping["env"]["PROJECT_KEY"], "ABCDEFGH")
|
self.assertEqual(self.project_mapping["env"]["PROJECT_KEY"], "ABCDEFGH")
|
||||||
self.assertEqual(self.project_mapping["PWD"], os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
|
self.assertEqual(
|
||||||
self.assertEqual(self.project_mapping["test_path"], os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
|
self.project_mapping["PWD"],
|
||||||
|
os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.project_mapping["test_path"],
|
||||||
|
os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from httprunner.loader import check
|
|||||||
|
|
||||||
|
|
||||||
class TestLoaderCheck(unittest.TestCase):
|
class TestLoaderCheck(unittest.TestCase):
|
||||||
|
|
||||||
def test_is_testcases(self):
|
def test_is_testcases(self):
|
||||||
data_structure = "path/to/file"
|
data_structure = "path/to/file"
|
||||||
self.assertFalse(check.is_test_content(data_structure))
|
self.assertFalse(check.is_test_content(data_structure))
|
||||||
@@ -12,11 +11,7 @@ class TestLoaderCheck(unittest.TestCase):
|
|||||||
self.assertFalse(check.is_test_content(data_structure))
|
self.assertFalse(check.is_test_content(data_structure))
|
||||||
|
|
||||||
data_structure = {
|
data_structure = {
|
||||||
"project_mapping": {
|
"project_mapping": {"PWD": "XXXXX", "functions": {}, "env": {}},
|
||||||
"PWD": "XXXXX",
|
|
||||||
"functions": {},
|
|
||||||
"env": {}
|
|
||||||
},
|
|
||||||
"testcases": [
|
"testcases": [
|
||||||
{ # testcase data structure
|
{ # testcase data structure
|
||||||
"config": {
|
"config": {
|
||||||
@@ -27,19 +22,19 @@ class TestLoaderCheck(unittest.TestCase):
|
|||||||
"teststeps": [
|
"teststeps": [
|
||||||
# test data structure
|
# test data structure
|
||||||
{
|
{
|
||||||
'name': 'test step desc1',
|
"name": "test step desc1",
|
||||||
'variables': [], # optional
|
"variables": [], # optional
|
||||||
'extract': {}, # optional
|
"extract": {}, # optional
|
||||||
'validate': [],
|
"validate": [],
|
||||||
'request': {
|
"request": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "https://docs.httprunner.org"
|
"url": "https://docs.httprunner.org",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
# test_dict2 # another test dict
|
# test_dict2 # another test dict
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
# testcase_dict_2 # another testcase dict
|
# testcase_dict_2 # another testcase dict
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
self.assertTrue(check.is_test_content(data_structure))
|
self.assertTrue(check.is_test_content(data_structure))
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ from httprunner.loader.buildup import load_test_file
|
|||||||
|
|
||||||
|
|
||||||
class TestFileLoader(unittest.TestCase):
|
class TestFileLoader(unittest.TestCase):
|
||||||
|
|
||||||
def test_load_yaml_file_file_format_error(self):
|
def test_load_yaml_file_file_format_error(self):
|
||||||
yaml_tmp_file = "tests/data/tmp.yml"
|
yaml_tmp_file = "tests/data/tmp.yml"
|
||||||
# create empty yaml file
|
# create empty yaml file
|
||||||
with open(yaml_tmp_file, 'w') as f:
|
with open(yaml_tmp_file, "w") as f:
|
||||||
f.write("")
|
f.write("")
|
||||||
|
|
||||||
with self.assertRaises(exceptions.FileFormatError):
|
with self.assertRaises(exceptions.FileFormatError):
|
||||||
@@ -20,7 +19,7 @@ class TestFileLoader(unittest.TestCase):
|
|||||||
os.remove(yaml_tmp_file)
|
os.remove(yaml_tmp_file)
|
||||||
|
|
||||||
# create invalid format yaml file
|
# create invalid format yaml file
|
||||||
with open(yaml_tmp_file, 'w') as f:
|
with open(yaml_tmp_file, "w") as f:
|
||||||
f.write("abc")
|
f.write("abc")
|
||||||
|
|
||||||
with self.assertRaises(exceptions.FileFormatError):
|
with self.assertRaises(exceptions.FileFormatError):
|
||||||
@@ -31,7 +30,7 @@ class TestFileLoader(unittest.TestCase):
|
|||||||
def test_load_json_file_file_format_error(self):
|
def test_load_json_file_file_format_error(self):
|
||||||
json_tmp_file = "tests/data/tmp.json"
|
json_tmp_file = "tests/data/tmp.json"
|
||||||
# create empty file
|
# create empty file
|
||||||
with open(json_tmp_file, 'w') as f:
|
with open(json_tmp_file, "w") as f:
|
||||||
f.write("")
|
f.write("")
|
||||||
|
|
||||||
with self.assertRaises(exceptions.FileFormatError):
|
with self.assertRaises(exceptions.FileFormatError):
|
||||||
@@ -40,7 +39,7 @@ class TestFileLoader(unittest.TestCase):
|
|||||||
os.remove(json_tmp_file)
|
os.remove(json_tmp_file)
|
||||||
|
|
||||||
# create empty json file
|
# create empty json file
|
||||||
with open(json_tmp_file, 'w') as f:
|
with open(json_tmp_file, "w") as f:
|
||||||
f.write("{}")
|
f.write("{}")
|
||||||
|
|
||||||
with self.assertRaises(exceptions.FileFormatError):
|
with self.assertRaises(exceptions.FileFormatError):
|
||||||
@@ -49,7 +48,7 @@ class TestFileLoader(unittest.TestCase):
|
|||||||
os.remove(json_tmp_file)
|
os.remove(json_tmp_file)
|
||||||
|
|
||||||
# create invalid format json file
|
# create invalid format json file
|
||||||
with open(json_tmp_file, 'w') as f:
|
with open(json_tmp_file, "w") as f:
|
||||||
f.write("abc")
|
f.write("abc")
|
||||||
|
|
||||||
with self.assertRaises(exceptions.FileFormatError):
|
with self.assertRaises(exceptions.FileFormatError):
|
||||||
@@ -58,62 +57,62 @@ class TestFileLoader(unittest.TestCase):
|
|||||||
os.remove(json_tmp_file)
|
os.remove(json_tmp_file)
|
||||||
|
|
||||||
def test_load_testcases_bad_filepath(self):
|
def test_load_testcases_bad_filepath(self):
|
||||||
testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo')
|
testcase_file_path = os.path.join(os.getcwd(), "tests/data/demo")
|
||||||
with self.assertRaises(exceptions.FileNotFound):
|
with self.assertRaises(exceptions.FileNotFound):
|
||||||
load.load_file(testcase_file_path)
|
load.load_file(testcase_file_path)
|
||||||
|
|
||||||
def test_load_json_testcases(self):
|
def test_load_json_testcases(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.json')
|
os.getcwd(), "tests/data/demo_testcase_hardcode.json"
|
||||||
|
)
|
||||||
testcases = load.load_file(testcase_file_path)
|
testcases = load.load_file(testcase_file_path)
|
||||||
self.assertEqual(len(testcases), 3)
|
self.assertEqual(len(testcases), 3)
|
||||||
test = testcases[0]["test"]
|
test = testcases[0]["test"]
|
||||||
self.assertIn('name', test)
|
self.assertIn("name", test)
|
||||||
self.assertIn('request', test)
|
self.assertIn("request", test)
|
||||||
self.assertIn('url', test['request'])
|
self.assertIn("url", test["request"])
|
||||||
self.assertIn('method', test['request'])
|
self.assertIn("method", test["request"])
|
||||||
|
|
||||||
def test_load_yaml_testcases(self):
|
def test_load_yaml_testcases(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.yml')
|
os.getcwd(), "tests/data/demo_testcase_hardcode.yml"
|
||||||
|
)
|
||||||
testcases = load.load_file(testcase_file_path)
|
testcases = load.load_file(testcase_file_path)
|
||||||
self.assertEqual(len(testcases), 3)
|
self.assertEqual(len(testcases), 3)
|
||||||
test = testcases[0]["test"]
|
test = testcases[0]["test"]
|
||||||
self.assertIn('name', test)
|
self.assertIn("name", test)
|
||||||
self.assertIn('request', test)
|
self.assertIn("request", test)
|
||||||
self.assertIn('url', test['request'])
|
self.assertIn("url", test["request"])
|
||||||
self.assertIn('method', test['request'])
|
self.assertIn("method", test["request"])
|
||||||
|
|
||||||
def test_load_csv_file_one_parameter(self):
|
def test_load_csv_file_one_parameter(self):
|
||||||
csv_file_path = os.path.join(
|
csv_file_path = os.path.join(os.getcwd(), "tests/data/user_agent.csv")
|
||||||
os.getcwd(), 'tests/data/user_agent.csv')
|
|
||||||
csv_content = load.load_file(csv_file_path)
|
csv_content = load.load_file(csv_file_path)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
csv_content,
|
csv_content,
|
||||||
[
|
[
|
||||||
{'user_agent': 'iOS/10.1'},
|
{"user_agent": "iOS/10.1"},
|
||||||
{'user_agent': 'iOS/10.2'},
|
{"user_agent": "iOS/10.2"},
|
||||||
{'user_agent': 'iOS/10.3'}
|
{"user_agent": "iOS/10.3"},
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_load_csv_file_multiple_parameters(self):
|
def test_load_csv_file_multiple_parameters(self):
|
||||||
csv_file_path = os.path.join(
|
csv_file_path = os.path.join(os.getcwd(), "tests/data/account.csv")
|
||||||
os.getcwd(), 'tests/data/account.csv')
|
|
||||||
csv_content = load.load_file(csv_file_path)
|
csv_content = load.load_file(csv_file_path)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
csv_content,
|
csv_content,
|
||||||
[
|
[
|
||||||
{'username': 'test1', 'password': '111111'},
|
{"username": "test1", "password": "111111"},
|
||||||
{'username': 'test2', 'password': '222222'},
|
{"username": "test2", "password": "222222"},
|
||||||
{'username': 'test3', 'password': '333333'}
|
{"username": "test3", "password": "333333"},
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_load_folder_files(self):
|
def test_load_folder_files(self):
|
||||||
folder = os.path.join(os.getcwd(), 'tests')
|
folder = os.path.join(os.getcwd(), "tests")
|
||||||
file1 = os.path.join(os.getcwd(), 'tests', 'test_utils.py')
|
file1 = os.path.join(os.getcwd(), "tests", "test_utils.py")
|
||||||
file2 = os.path.join(os.getcwd(), 'tests', 'api', 'reset_all.yml')
|
file2 = os.path.join(os.getcwd(), "tests", "api", "reset_all.yml")
|
||||||
|
|
||||||
files = load.load_folder_files(folder, recursive=False)
|
files = load.load_folder_files(folder, recursive=False)
|
||||||
self.assertEqual(files, [])
|
self.assertEqual(files, [])
|
||||||
@@ -129,25 +128,25 @@ class TestFileLoader(unittest.TestCase):
|
|||||||
self.assertEqual([], files)
|
self.assertEqual([], files)
|
||||||
|
|
||||||
def test_load_dot_env_file(self):
|
def test_load_dot_env_file(self):
|
||||||
dot_env_path = os.path.join(
|
dot_env_path = os.path.join(os.getcwd(), "tests", ".env")
|
||||||
os.getcwd(), "tests", ".env"
|
|
||||||
)
|
|
||||||
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
||||||
self.assertIn("PROJECT_KEY", env_variables_mapping)
|
self.assertIn("PROJECT_KEY", env_variables_mapping)
|
||||||
self.assertEqual(env_variables_mapping["UserName"], "debugtalk")
|
self.assertEqual(env_variables_mapping["UserName"], "debugtalk")
|
||||||
|
|
||||||
def test_load_custom_dot_env_file(self):
|
def test_load_custom_dot_env_file(self):
|
||||||
dot_env_path = os.path.join(
|
dot_env_path = os.path.join(os.getcwd(), "tests", "data", "test.env")
|
||||||
os.getcwd(), "tests", "data", "test.env"
|
|
||||||
)
|
|
||||||
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
||||||
self.assertIn("PROJECT_KEY", env_variables_mapping)
|
self.assertIn("PROJECT_KEY", env_variables_mapping)
|
||||||
self.assertEqual(env_variables_mapping["UserName"], "test")
|
self.assertEqual(env_variables_mapping["UserName"], "test")
|
||||||
self.assertEqual(env_variables_mapping["content_type"], "application/json; charset=UTF-8")
|
self.assertEqual(
|
||||||
|
env_variables_mapping["content_type"], "application/json; charset=UTF-8"
|
||||||
|
)
|
||||||
|
|
||||||
def test_load_env_path_not_exist(self):
|
def test_load_env_path_not_exist(self):
|
||||||
dot_env_path = os.path.join(
|
dot_env_path = os.path.join(
|
||||||
os.getcwd(), "tests", "data",
|
os.getcwd(),
|
||||||
|
"tests",
|
||||||
|
"data",
|
||||||
)
|
)
|
||||||
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
||||||
self.assertEqual(env_variables_mapping, {})
|
self.assertEqual(env_variables_mapping, {})
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@@ -7,7 +6,6 @@ from httprunner.loader import locate
|
|||||||
|
|
||||||
|
|
||||||
class TestLoaderLocate(unittest.TestCase):
|
class TestLoaderLocate(unittest.TestCase):
|
||||||
|
|
||||||
def test_locate_file(self):
|
def test_locate_file(self):
|
||||||
with self.assertRaises(exceptions.FileNotFound):
|
with self.assertRaises(exceptions.FileNotFound):
|
||||||
locate.locate_file(os.getcwd(), "debugtalk.py")
|
locate.locate_file(os.getcwd(), "debugtalk.py")
|
||||||
@@ -18,23 +16,21 @@ class TestLoaderLocate(unittest.TestCase):
|
|||||||
start_path = os.path.join(os.getcwd(), "tests")
|
start_path = os.path.join(os.getcwd(), "tests")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
locate.locate_file(start_path, "debugtalk.py"),
|
locate.locate_file(start_path, "debugtalk.py"),
|
||||||
os.path.join(
|
os.path.join(os.getcwd(), "tests/debugtalk.py"),
|
||||||
os.getcwd(), "tests/debugtalk.py"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
locate.locate_file("tests/", "debugtalk.py"),
|
locate.locate_file("tests/", "debugtalk.py"),
|
||||||
os.path.join(os.getcwd(), "tests", "debugtalk.py")
|
os.path.join(os.getcwd(), "tests", "debugtalk.py"),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
locate.locate_file("tests", "debugtalk.py"),
|
locate.locate_file("tests", "debugtalk.py"),
|
||||||
os.path.join(os.getcwd(), "tests", "debugtalk.py")
|
os.path.join(os.getcwd(), "tests", "debugtalk.py"),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
locate.locate_file("tests/base.py", "debugtalk.py"),
|
locate.locate_file("tests/base.py", "debugtalk.py"),
|
||||||
os.path.join(os.getcwd(), "tests", "debugtalk.py")
|
os.path.join(os.getcwd(), "tests", "debugtalk.py"),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
locate.locate_file("tests/data/demo_testcase.yml", "debugtalk.py"),
|
locate.locate_file("tests/data/demo_testcase.yml", "debugtalk.py"),
|
||||||
os.path.join(os.getcwd(), "tests", "debugtalk.py")
|
os.path.join(os.getcwd(), "tests", "debugtalk.py"),
|
||||||
)
|
)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,17 +7,16 @@ from tests.base import ApiServerUnittest
|
|||||||
|
|
||||||
|
|
||||||
class TestResponse(ApiServerUnittest):
|
class TestResponse(ApiServerUnittest):
|
||||||
|
|
||||||
def test_parse_response_object_json(self):
|
def test_parse_response_object_json(self):
|
||||||
url = "http://127.0.0.1:5000/api/users"
|
url = "http://127.0.0.1:5000/api/users"
|
||||||
resp = requests.get(url)
|
resp = requests.get(url)
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
self.assertTrue(hasattr(resp_obj, 'status_code'))
|
self.assertTrue(hasattr(resp_obj, "status_code"))
|
||||||
self.assertTrue(hasattr(resp_obj, 'headers'))
|
self.assertTrue(hasattr(resp_obj, "headers"))
|
||||||
self.assertTrue(hasattr(resp_obj, 'content'))
|
self.assertTrue(hasattr(resp_obj, "content"))
|
||||||
self.assertIn('Content-Type', resp_obj.headers)
|
self.assertIn("Content-Type", resp_obj.headers)
|
||||||
self.assertIn('Content-Length', resp_obj.headers)
|
self.assertIn("Content-Length", resp_obj.headers)
|
||||||
self.assertIn('success', resp_obj.json)
|
self.assertIn("success", resp_obj.json)
|
||||||
|
|
||||||
def test_parse_response_object_content(self):
|
def test_parse_response_object_content(self):
|
||||||
url = "http://127.0.0.1:5000/"
|
url = "http://127.0.0.1:5000/"
|
||||||
@@ -29,19 +28,12 @@ class TestResponse(ApiServerUnittest):
|
|||||||
resp = requests.get(url="{}/status/200".format(HTTPBIN_SERVER))
|
resp = requests.get(url="{}/status/200".format(HTTPBIN_SERVER))
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_status_code": "status_code"}]
|
||||||
{"resp_status_code": "status_code"}
|
|
||||||
]
|
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(extract_binds_dict["resp_status_code"], 200)
|
||||||
extract_binds_dict["resp_status_code"],
|
|
||||||
200
|
|
||||||
)
|
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_status_code": "status_code.xx"}]
|
||||||
{"resp_status_code": "status_code.xx"}
|
|
||||||
]
|
|
||||||
with self.assertRaises(exceptions.ParamsError):
|
with self.assertRaises(exceptions.ParamsError):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
@@ -53,14 +45,16 @@ class TestResponse(ApiServerUnittest):
|
|||||||
{"resp_encoding": "encoding"},
|
{"resp_encoding": "encoding"},
|
||||||
{"resp_ok": "ok"},
|
{"resp_ok": "ok"},
|
||||||
{"resp_reason": "reason"},
|
{"resp_reason": "reason"},
|
||||||
{"resp_url": "url"}
|
{"resp_url": "url"},
|
||||||
]
|
]
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
self.assertEqual(extract_binds_dict["resp_encoding"], "utf-8")
|
self.assertEqual(extract_binds_dict["resp_encoding"], "utf-8")
|
||||||
self.assertEqual(extract_binds_dict["resp_ok"], True)
|
self.assertEqual(extract_binds_dict["resp_ok"], True)
|
||||||
self.assertEqual(extract_binds_dict["resp_reason"], "OK")
|
self.assertEqual(extract_binds_dict["resp_reason"], "OK")
|
||||||
self.assertEqual(extract_binds_dict["resp_url"], "{}/status/200".format(HTTPBIN_SERVER))
|
self.assertEqual(
|
||||||
|
extract_binds_dict["resp_url"], "{}/status/200".format(HTTPBIN_SERVER)
|
||||||
|
)
|
||||||
|
|
||||||
extract_binds_list = [{"resp_encoding": "encoding.xx"}]
|
extract_binds_list = [{"resp_encoding": "encoding.xx"}]
|
||||||
with self.assertRaises(exceptions.ParamsError):
|
with self.assertRaises(exceptions.ParamsError):
|
||||||
@@ -81,24 +75,15 @@ class TestResponse(ApiServerUnittest):
|
|||||||
def test_extract_response_cookies(self):
|
def test_extract_response_cookies(self):
|
||||||
resp = requests.get(
|
resp = requests.get(
|
||||||
url="{}/cookies".format(HTTPBIN_SERVER),
|
url="{}/cookies".format(HTTPBIN_SERVER),
|
||||||
headers={
|
headers={"accept": "application/json"},
|
||||||
"accept": "application/json"
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_cookies": "cookies"}]
|
||||||
{"resp_cookies": "cookies"}
|
|
||||||
]
|
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
self.assertEqual(
|
self.assertEqual(extract_binds_dict["resp_cookies"], {})
|
||||||
extract_binds_dict["resp_cookies"],
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_cookies": "cookies.xx"}]
|
||||||
{"resp_cookies": "cookies.xx"}
|
|
||||||
]
|
|
||||||
with self.assertRaises(exceptions.ExtractFailure):
|
with self.assertRaises(exceptions.ExtractFailure):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
@@ -106,22 +91,20 @@ class TestResponse(ApiServerUnittest):
|
|||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
url="{}/anything".format(HTTPBIN_SERVER),
|
url="{}/anything".format(HTTPBIN_SERVER),
|
||||||
json={
|
json={
|
||||||
'success': False,
|
"success": False,
|
||||||
"person": {
|
"person": {
|
||||||
"name": {
|
"name": {
|
||||||
"first_name": "Leo",
|
"first_name": "Leo",
|
||||||
"last_name": "Lee",
|
"last_name": "Lee",
|
||||||
},
|
},
|
||||||
"age": 29,
|
"age": 29,
|
||||||
"cities": ["Guangzhou", "Shenzhen"]
|
"cities": ["Guangzhou", "Shenzhen"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_elapsed": "elapsed"}]
|
||||||
{"resp_elapsed": "elapsed"}
|
|
||||||
]
|
|
||||||
with self.assertRaises(exceptions.ParamsError):
|
with self.assertRaises(exceptions.ParamsError):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
@@ -129,7 +112,7 @@ class TestResponse(ApiServerUnittest):
|
|||||||
{"resp_elapsed_microseconds": "elapsed.microseconds"},
|
{"resp_elapsed_microseconds": "elapsed.microseconds"},
|
||||||
{"resp_elapsed_seconds": "elapsed.seconds"},
|
{"resp_elapsed_seconds": "elapsed.seconds"},
|
||||||
{"resp_elapsed_days": "elapsed.days"},
|
{"resp_elapsed_days": "elapsed.days"},
|
||||||
{"resp_elapsed_total_seconds": "elapsed.total_seconds"}
|
{"resp_elapsed_total_seconds": "elapsed.total_seconds"},
|
||||||
]
|
]
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
self.assertGreater(extract_binds_dict["resp_elapsed_microseconds"], 1000)
|
self.assertGreater(extract_binds_dict["resp_elapsed_microseconds"], 1000)
|
||||||
@@ -137,9 +120,7 @@ class TestResponse(ApiServerUnittest):
|
|||||||
self.assertEqual(extract_binds_dict["resp_elapsed_days"], 0)
|
self.assertEqual(extract_binds_dict["resp_elapsed_days"], 0)
|
||||||
self.assertGreater(extract_binds_dict["resp_elapsed_total_seconds"], 0)
|
self.assertGreater(extract_binds_dict["resp_elapsed_total_seconds"], 0)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_elapsed": "elapsed.years"}]
|
||||||
{"resp_elapsed": "elapsed.years"}
|
|
||||||
]
|
|
||||||
with self.assertRaises(exceptions.ParamsError):
|
with self.assertRaises(exceptions.ParamsError):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
@@ -150,16 +131,16 @@ class TestResponse(ApiServerUnittest):
|
|||||||
extract_binds_list = [
|
extract_binds_list = [
|
||||||
{"resp_headers": "headers"},
|
{"resp_headers": "headers"},
|
||||||
{"resp_headers_content_type": "headers.Content-Type"},
|
{"resp_headers_content_type": "headers.Content-Type"},
|
||||||
{"resp_headers_content_type_lowercase": "headers.content-type"}
|
{"resp_headers_content_type_lowercase": "headers.content-type"},
|
||||||
]
|
]
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
self.assertIn("Content-Type", extract_binds_dict["resp_headers"])
|
self.assertIn("Content-Type", extract_binds_dict["resp_headers"])
|
||||||
self.assertIn("text/html", extract_binds_dict["resp_headers_content_type"])
|
self.assertIn("text/html", extract_binds_dict["resp_headers_content_type"])
|
||||||
self.assertIn("text/html", extract_binds_dict["resp_headers_content_type_lowercase"])
|
self.assertIn(
|
||||||
|
"text/html", extract_binds_dict["resp_headers_content_type_lowercase"]
|
||||||
|
)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_headers_xxx": "headers.xxx"}]
|
||||||
{"resp_headers_xxx": "headers.xxx"}
|
|
||||||
]
|
|
||||||
with self.assertRaises(exceptions.ExtractFailure):
|
with self.assertRaises(exceptions.ExtractFailure):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
@@ -167,16 +148,16 @@ class TestResponse(ApiServerUnittest):
|
|||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
url="{}/anything".format(HTTPBIN_SERVER),
|
url="{}/anything".format(HTTPBIN_SERVER),
|
||||||
json={
|
json={
|
||||||
'success': False,
|
"success": False,
|
||||||
"person": {
|
"person": {
|
||||||
"name": {
|
"name": {
|
||||||
"first_name": "Leo",
|
"first_name": "Leo",
|
||||||
"last_name": "Lee",
|
"last_name": "Lee",
|
||||||
},
|
},
|
||||||
"age": 29,
|
"age": 29,
|
||||||
"cities": ["Guangzhou", "Shenzhen"]
|
"cities": ["Guangzhou", "Shenzhen"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
# resp.json()
|
# resp.json()
|
||||||
# {
|
# {
|
||||||
@@ -218,51 +199,31 @@ class TestResponse(ApiServerUnittest):
|
|||||||
{"resp_content_content_success": "content.json.success"},
|
{"resp_content_content_success": "content.json.success"},
|
||||||
{"resp_content_text_success": "text.json.success"},
|
{"resp_content_text_success": "text.json.success"},
|
||||||
{"resp_content_person_first_name": "content.json.person.name.first_name"},
|
{"resp_content_person_first_name": "content.json.person.name.first_name"},
|
||||||
{"resp_content_cities_1": "content.json.person.cities.1"}
|
{"resp_content_cities_1": "content.json.person.cities.1"},
|
||||||
]
|
]
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
extract_binds_dict["resp_headers_content_type"],
|
extract_binds_dict["resp_headers_content_type"], "application/json"
|
||||||
"application/json"
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
extract_binds_dict["resp_content_body_success"],
|
|
||||||
False
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
extract_binds_dict["resp_content_content_success"],
|
|
||||||
False
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
extract_binds_dict["resp_content_text_success"],
|
|
||||||
False
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
extract_binds_dict["resp_content_person_first_name"],
|
|
||||||
"Leo"
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
extract_binds_dict["resp_content_cities_1"],
|
|
||||||
"Shenzhen"
|
|
||||||
)
|
)
|
||||||
|
self.assertEqual(extract_binds_dict["resp_content_body_success"], False)
|
||||||
|
self.assertEqual(extract_binds_dict["resp_content_content_success"], False)
|
||||||
|
self.assertEqual(extract_binds_dict["resp_content_text_success"], False)
|
||||||
|
self.assertEqual(extract_binds_dict["resp_content_person_first_name"], "Leo")
|
||||||
|
self.assertEqual(extract_binds_dict["resp_content_cities_1"], "Shenzhen")
|
||||||
|
|
||||||
def test_extract_response_body_html(self):
|
def test_extract_response_body_html(self):
|
||||||
resp = requests.get(url=HTTPBIN_SERVER)
|
resp = requests.get(url=HTTPBIN_SERVER)
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_content": "content"}]
|
||||||
{"resp_content": "content"}
|
|
||||||
]
|
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
self.assertIsInstance(extract_binds_dict["resp_content"], basestring)
|
self.assertIsInstance(extract_binds_dict["resp_content"], basestring)
|
||||||
self.assertIn("httpbin.org", extract_binds_dict["resp_content"])
|
self.assertIn("httpbin.org", extract_binds_dict["resp_content"])
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_content": "content.xxx"}]
|
||||||
{"resp_content": "content.xxx"}
|
|
||||||
]
|
|
||||||
with self.assertRaises(exceptions.ExtractFailure):
|
with self.assertRaises(exceptions.ExtractFailure):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
@@ -272,7 +233,7 @@ class TestResponse(ApiServerUnittest):
|
|||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [
|
||||||
{"resp_others_encoding": "encoding"},
|
{"resp_others_encoding": "encoding"},
|
||||||
{"resp_others_history": "history"}
|
{"resp_others_history": "history"},
|
||||||
]
|
]
|
||||||
with self.assertRaises(exceptions.ParamsError):
|
with self.assertRaises(exceptions.ParamsError):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
@@ -281,21 +242,19 @@ class TestResponse(ApiServerUnittest):
|
|||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
url="{}/anything".format(HTTPBIN_SERVER),
|
url="{}/anything".format(HTTPBIN_SERVER),
|
||||||
json={
|
json={
|
||||||
'success': False,
|
"success": False,
|
||||||
"person": {
|
"person": {
|
||||||
"name": {
|
"name": {
|
||||||
"first_name": "Leo",
|
"first_name": "Leo",
|
||||||
"last_name": "Lee",
|
"last_name": "Lee",
|
||||||
},
|
},
|
||||||
"age": 29,
|
"age": 29,
|
||||||
"cities": ["Guangzhou", "Shenzhen"]
|
"cities": ["Guangzhou", "Shenzhen"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_content_dict_key_error": "content.not_exist"}]
|
||||||
{"resp_content_dict_key_error": "content.not_exist"}
|
|
||||||
]
|
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
with self.assertRaises(exceptions.ExtractFailure):
|
with self.assertRaises(exceptions.ExtractFailure):
|
||||||
@@ -310,80 +269,49 @@ class TestResponse(ApiServerUnittest):
|
|||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
def test_extract_response_json_string(self):
|
def test_extract_response_json_string(self):
|
||||||
resp = requests.post(
|
resp = requests.post(url="{}/anything".format(HTTPBIN_SERVER), data="abc")
|
||||||
url="{}/anything".format(HTTPBIN_SERVER),
|
|
||||||
data="abc"
|
|
||||||
)
|
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_content_body": "content.data"}]
|
||||||
{"resp_content_body": "content.data"}
|
|
||||||
]
|
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
self.assertEqual(
|
self.assertEqual(extract_binds_dict["resp_content_body"], "abc")
|
||||||
extract_binds_dict["resp_content_body"],
|
|
||||||
"abc"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_extract_text_response(self):
|
def test_extract_text_response(self):
|
||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
url="{}/anything".format(HTTPBIN_SERVER),
|
url="{}/anything".format(HTTPBIN_SERVER), data="LB123abcRB789"
|
||||||
data="LB123abcRB789"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [
|
||||||
{"resp_content_key1": "LB123(.*)RB789"},
|
{"resp_content_key1": "LB123(.*)RB789"},
|
||||||
{"resp_content_key2": "LB[\d]*(.*)RB[\d]*"},
|
{"resp_content_key2": "LB[\d]*(.*)RB[\d]*"},
|
||||||
{"resp_content_key3": "LB[\d]*(.*)9"}
|
{"resp_content_key3": "LB[\d]*(.*)9"},
|
||||||
]
|
]
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
|
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
self.assertEqual(
|
self.assertEqual(extract_binds_dict["resp_content_key1"], "abc")
|
||||||
extract_binds_dict["resp_content_key1"],
|
self.assertEqual(extract_binds_dict["resp_content_key2"], "abc")
|
||||||
"abc"
|
self.assertEqual(extract_binds_dict["resp_content_key3"], "abcRB78")
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
extract_binds_dict["resp_content_key2"],
|
|
||||||
"abc"
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
extract_binds_dict["resp_content_key3"],
|
|
||||||
"abcRB78"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_extract_text_response_exception(self):
|
def test_extract_text_response_exception(self):
|
||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
url="{}/anything".format(HTTPBIN_SERVER),
|
url="{}/anything".format(HTTPBIN_SERVER), data="LB123abcRB789"
|
||||||
data="LB123abcRB789"
|
|
||||||
)
|
)
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_content_key1": "LB123.*RB789"}]
|
||||||
{"resp_content_key1": "LB123.*RB789"}
|
|
||||||
]
|
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
with self.assertRaises(exceptions.ParamsError):
|
with self.assertRaises(exceptions.ParamsError):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|
||||||
def test_extract_response_empty(self):
|
def test_extract_response_empty(self):
|
||||||
resp = requests.post(
|
resp = requests.post(url="{}/anything".format(HTTPBIN_SERVER), data="abc")
|
||||||
url="{}/anything".format(HTTPBIN_SERVER),
|
|
||||||
data="abc"
|
|
||||||
)
|
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_content_body": "content.data"}]
|
||||||
{"resp_content_body": "content.data"}
|
|
||||||
]
|
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||||
self.assertEqual(
|
self.assertEqual(extract_binds_dict["resp_content_body"], "abc")
|
||||||
extract_binds_dict["resp_content_body"],
|
|
||||||
'abc'
|
|
||||||
)
|
|
||||||
|
|
||||||
extract_binds_list = [
|
extract_binds_list = [{"resp_content_body": "content.data.def"}]
|
||||||
{"resp_content_body": "content.data.def"}
|
|
||||||
]
|
|
||||||
resp_obj = response.ResponseObject(resp)
|
resp_obj = response.ResponseObject(resp)
|
||||||
with self.assertRaises(exceptions.ExtractFailure):
|
with self.assertRaises(exceptions.ExtractFailure):
|
||||||
resp_obj.extract_response(extract_binds_list)
|
resp_obj.extract_response(extract_binds_list)
|
||||||
|
|||||||
@@ -7,16 +7,11 @@ from tests.base import ApiServerUnittest
|
|||||||
|
|
||||||
|
|
||||||
class TestRunner(ApiServerUnittest):
|
class TestRunner(ApiServerUnittest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
project_mapping = loader.load_project_data(os.path.join(os.getcwd(), "tests"))
|
project_mapping = loader.load_project_data(os.path.join(os.getcwd(), "tests"))
|
||||||
self.debugtalk_functions = project_mapping["functions"]
|
self.debugtalk_functions = project_mapping["functions"]
|
||||||
|
|
||||||
config = {
|
config = {"name": "XXX", "base_url": "http://127.0.0.1", "verify": False}
|
||||||
"name": "XXX",
|
|
||||||
"base_url": "http://127.0.0.1",
|
|
||||||
"verify": False
|
|
||||||
}
|
|
||||||
self.test_runner = runner.Runner(config)
|
self.test_runner = runner.Runner(config)
|
||||||
self.reset_all()
|
self.reset_all()
|
||||||
|
|
||||||
@@ -27,10 +22,8 @@ class TestRunner(ApiServerUnittest):
|
|||||||
|
|
||||||
def test_run_single_testcase(self):
|
def test_run_single_testcase(self):
|
||||||
testcase_file_path_list = [
|
testcase_file_path_list = [
|
||||||
os.path.join(
|
os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.yml"),
|
||||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.yml'),
|
os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.json"),
|
||||||
os.path.join(
|
|
||||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.json')
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for testcase_file_path in testcase_file_path_list:
|
for testcase_file_path in testcase_file_path_list:
|
||||||
@@ -50,14 +43,8 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"config": {
|
"config": {
|
||||||
"name": "basic test with httpbin",
|
"name": "basic test with httpbin",
|
||||||
"base_url": HTTPBIN_SERVER,
|
"base_url": HTTPBIN_SERVER,
|
||||||
"setup_hooks": [
|
"setup_hooks": ["${sleep(0.5)}", "${hook_print(setup)}"],
|
||||||
"${sleep(0.5)}",
|
"teardown_hooks": ["${sleep(1)}", "${hook_print(teardown)}"],
|
||||||
"${hook_print(setup)}"
|
|
||||||
],
|
|
||||||
"teardown_hooks": [
|
|
||||||
"${sleep(1)}",
|
|
||||||
"${hook_print(teardown)}"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
@@ -70,24 +57,20 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"user_agent": "iOS/10.3",
|
"user_agent": "iOS/10.3",
|
||||||
"device_sn": "HZfFBh6tU59EdXJ",
|
"device_sn": "HZfFBh6tU59EdXJ",
|
||||||
"os_platform": "ios",
|
"os_platform": "ios",
|
||||||
"app_version": "2.8.6"
|
"app_version": "2.8.6",
|
||||||
},
|
},
|
||||||
"json": {
|
"json": {
|
||||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"check": "status_code", "expect": 200}],
|
||||||
{"check": "status_code", "expect": 200}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {"functions": self.debugtalk_functions},
|
||||||
"functions": self.debugtalk_functions
|
"testcases": testcases,
|
||||||
},
|
|
||||||
"testcases": testcases
|
|
||||||
}
|
}
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
@@ -107,7 +90,7 @@ class TestRunner(ApiServerUnittest):
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"name": "basic test with httpbin",
|
"name": "basic test with httpbin",
|
||||||
"base_url": HTTPBIN_SERVER
|
"base_url": HTTPBIN_SERVER,
|
||||||
},
|
},
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
@@ -116,27 +99,18 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"request": {
|
"request": {
|
||||||
"url": "/anything",
|
"url": "/anything",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {"user_agent": "iOS/10.3", "os_platform": "ios"},
|
||||||
"user_agent": "iOS/10.3",
|
"data": "a=1&b=2",
|
||||||
"os_platform": "ios"
|
|
||||||
},
|
|
||||||
"data": "a=1&b=2"
|
|
||||||
},
|
},
|
||||||
"setup_hooks": [
|
"setup_hooks": [{"total": "${sum_two(1, 5)}"}],
|
||||||
{"total": "${sum_two(1, 5)}"}
|
"validate": [{"check": "status_code", "expect": 200}],
|
||||||
],
|
|
||||||
"validate": [
|
|
||||||
{"check": "status_code", "expect": 200}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {"functions": self.debugtalk_functions},
|
||||||
"functions": self.debugtalk_functions
|
"testcases": testcases,
|
||||||
},
|
|
||||||
"testcases": testcases
|
|
||||||
}
|
}
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
@@ -151,7 +125,7 @@ class TestRunner(ApiServerUnittest):
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"name": "basic test with httpbin",
|
"name": "basic test with httpbin",
|
||||||
"base_url": HTTPBIN_SERVER
|
"base_url": HTTPBIN_SERVER,
|
||||||
},
|
},
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
@@ -162,29 +136,25 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"user_agent": "iOS/10.3"
|
"user_agent": "iOS/10.3",
|
||||||
},
|
},
|
||||||
"json": {
|
"json": {
|
||||||
"os_platform": "ios",
|
"os_platform": "ios",
|
||||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"setup_hooks": [
|
"setup_hooks": ["${modify_request_json($request, android)}"],
|
||||||
"${modify_request_json($request, android)}"
|
|
||||||
],
|
|
||||||
"validate": [
|
"validate": [
|
||||||
{"check": "status_code", "expect": 200},
|
{"check": "status_code", "expect": 200},
|
||||||
{"check": "content.json.os_platform", "expect": "android"}
|
{"check": "content.json.os_platform", "expect": "android"},
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {"functions": self.debugtalk_functions},
|
||||||
"functions": self.debugtalk_functions
|
"testcases": testcases,
|
||||||
},
|
|
||||||
"testcases": testcases
|
|
||||||
}
|
}
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
@@ -194,9 +164,7 @@ class TestRunner(ApiServerUnittest):
|
|||||||
def test_run_testcase_with_teardown_hooks_success(self):
|
def test_run_testcase_with_teardown_hooks_success(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "basic test with httpbin"},
|
||||||
"name": "basic test with httpbin"
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "get token",
|
"name": "get token",
|
||||||
@@ -208,25 +176,23 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"user_agent": "iOS/10.3",
|
"user_agent": "iOS/10.3",
|
||||||
"device_sn": "HZfFBh6tU59EdXJ",
|
"device_sn": "HZfFBh6tU59EdXJ",
|
||||||
"os_platform": "ios",
|
"os_platform": "ios",
|
||||||
"app_version": "2.8.6"
|
"app_version": "2.8.6",
|
||||||
},
|
},
|
||||||
"json": {
|
"json": {
|
||||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"check": "status_code", "expect": 200}],
|
||||||
{"check": "status_code", "expect": 200}
|
"teardown_hooks": [
|
||||||
|
"${teardown_hook_sleep_N_secs($response, 2)}"
|
||||||
],
|
],
|
||||||
"teardown_hooks": ["${teardown_hook_sleep_N_secs($response, 2)}"]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {"functions": self.debugtalk_functions},
|
||||||
"functions": self.debugtalk_functions
|
"testcases": testcases,
|
||||||
},
|
|
||||||
"testcases": testcases
|
|
||||||
}
|
}
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
@@ -241,9 +207,7 @@ class TestRunner(ApiServerUnittest):
|
|||||||
def test_run_testcase_with_teardown_hooks_fail(self):
|
def test_run_testcase_with_teardown_hooks_fail(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {"name": "basic test with httpbin"},
|
||||||
"name": "basic test with httpbin"
|
|
||||||
},
|
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
"name": "get token",
|
"name": "get token",
|
||||||
@@ -255,25 +219,23 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"user_agent": "iOS/10.3",
|
"user_agent": "iOS/10.3",
|
||||||
"device_sn": "HZfFBh6tU59EdXJ",
|
"device_sn": "HZfFBh6tU59EdXJ",
|
||||||
"os_platform": "ios",
|
"os_platform": "ios",
|
||||||
"app_version": "2.8.6"
|
"app_version": "2.8.6",
|
||||||
},
|
},
|
||||||
"json": {
|
"json": {
|
||||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"check": "status_code", "expect": 404}],
|
||||||
{"check": "status_code", "expect": 404}
|
"teardown_hooks": [
|
||||||
|
"${teardown_hook_sleep_N_secs($response, 2)}"
|
||||||
],
|
],
|
||||||
"teardown_hooks": ["${teardown_hook_sleep_N_secs($response, 2)}"]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {"functions": self.debugtalk_functions},
|
||||||
"functions": self.debugtalk_functions
|
"testcases": testcases,
|
||||||
},
|
|
||||||
"testcases": testcases
|
|
||||||
}
|
}
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
@@ -287,7 +249,8 @@ class TestRunner(ApiServerUnittest):
|
|||||||
|
|
||||||
def test_bugfix_type_match(self):
|
def test_bugfix_type_match(self):
|
||||||
testcase_file_path = os.path.join(
|
testcase_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/bugfix_type_match.yml')
|
os.getcwd(), "tests/data/bugfix_type_match.yml"
|
||||||
|
)
|
||||||
tests_mapping = loader.load_cases(testcase_file_path)
|
tests_mapping = loader.load_cases(testcase_file_path)
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
@@ -309,28 +272,38 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"user_agent": "iOS/10.3",
|
"user_agent": "iOS/10.3",
|
||||||
"device_sn": "HZfFBh6tU59EdXJ",
|
"device_sn": "HZfFBh6tU59EdXJ",
|
||||||
"os_platform": "ios",
|
"os_platform": "ios",
|
||||||
"app_version": "2.8.6"
|
"app_version": "2.8.6",
|
||||||
},
|
},
|
||||||
"json": {
|
"json": {
|
||||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [
|
||||||
{"check": "status_code", "expect": 200},
|
{"check": "status_code", "expect": 200},
|
||||||
{"check": "elapsed.seconds", "comparator": "lt", "expect": 1},
|
{
|
||||||
|
"check": "elapsed.seconds",
|
||||||
|
"comparator": "lt",
|
||||||
|
"expect": 1,
|
||||||
|
},
|
||||||
{"check": "elapsed.days", "comparator": "eq", "expect": 0},
|
{"check": "elapsed.days", "comparator": "eq", "expect": 0},
|
||||||
{"check": "elapsed.microseconds", "comparator": "gt", "expect": 1000},
|
{
|
||||||
{"check": "elapsed.total_seconds", "comparator": "lt", "expect": 1}
|
"check": "elapsed.microseconds",
|
||||||
]
|
"comparator": "gt",
|
||||||
|
"expect": 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"check": "elapsed.total_seconds",
|
||||||
|
"comparator": "lt",
|
||||||
|
"expect": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {"functions": self.debugtalk_functions},
|
||||||
"functions": self.debugtalk_functions
|
"testcases": testcases,
|
||||||
},
|
|
||||||
"testcases": testcases
|
|
||||||
}
|
}
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
@@ -343,7 +316,7 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"config": {
|
"config": {
|
||||||
"name": "basic test with httpbin",
|
"name": "basic test with httpbin",
|
||||||
"base_url": HTTPBIN_SERVER,
|
"base_url": HTTPBIN_SERVER,
|
||||||
"variables": "${gen_variables()}"
|
"variables": "${gen_variables()}",
|
||||||
},
|
},
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
{
|
{
|
||||||
@@ -352,24 +325,17 @@ class TestRunner(ApiServerUnittest):
|
|||||||
"request": {
|
"request": {
|
||||||
"url": "/anything",
|
"url": "/anything",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {"user_agent": "iOS/10.3", "os_platform": "ios"},
|
||||||
"user_agent": "iOS/10.3",
|
"data": "a=1&b=2",
|
||||||
"os_platform": "ios"
|
|
||||||
},
|
|
||||||
"data": "a=1&b=2"
|
|
||||||
},
|
},
|
||||||
"validate": [
|
"validate": [{"check": "status_code", "expect": 200}],
|
||||||
{"check": "status_code", "expect": 200}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
tests_mapping = {
|
tests_mapping = {
|
||||||
"project_mapping": {
|
"project_mapping": {"functions": self.debugtalk_functions},
|
||||||
"functions": self.debugtalk_functions
|
"testcases": testcases,
|
||||||
},
|
|
||||||
"testcases": testcases
|
|
||||||
}
|
}
|
||||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
|
|||||||
@@ -7,12 +7,9 @@ from tests.base import ApiServerUnittest
|
|||||||
|
|
||||||
|
|
||||||
class TestUtils(ApiServerUnittest):
|
class TestUtils(ApiServerUnittest):
|
||||||
|
|
||||||
def test_set_os_environ(self):
|
def test_set_os_environ(self):
|
||||||
self.assertNotIn("abc", os.environ)
|
self.assertNotIn("abc", os.environ)
|
||||||
variables_mapping = {
|
variables_mapping = {"abc": "123"}
|
||||||
"abc": "123"
|
|
||||||
}
|
|
||||||
utils.set_os_environ(variables_mapping)
|
utils.set_os_environ(variables_mapping)
|
||||||
self.assertIn("abc", os.environ)
|
self.assertIn("abc", os.environ)
|
||||||
self.assertEqual(os.environ["abc"], "123")
|
self.assertEqual(os.environ["abc"], "123")
|
||||||
@@ -26,8 +23,8 @@ class TestUtils(ApiServerUnittest):
|
|||||||
"last_name": "Lee",
|
"last_name": "Lee",
|
||||||
},
|
},
|
||||||
"age": 29,
|
"age": 29,
|
||||||
"cities": ["Guangzhou", "Shenzhen"]
|
"cities": ["Guangzhou", "Shenzhen"],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
query = "ids.2"
|
query = "ids.2"
|
||||||
result = utils.query_json(json_content, query)
|
result = utils.query_json(json_content, query)
|
||||||
@@ -63,6 +60,7 @@ class TestUtils(ApiServerUnittest):
|
|||||||
|
|
||||||
def current_validators(self):
|
def current_validators(self):
|
||||||
from httprunner.builtin import comparators
|
from httprunner.builtin import comparators
|
||||||
|
|
||||||
functions_mapping = loader.load.load_module_functions(comparators)
|
functions_mapping = loader.load.load_module_functions(comparators)
|
||||||
|
|
||||||
functions_mapping["equals"](None, None)
|
functions_mapping["equals"](None, None)
|
||||||
@@ -80,17 +78,17 @@ class TestUtils(ApiServerUnittest):
|
|||||||
functions_mapping["not_equals"](123, "123")
|
functions_mapping["not_equals"](123, "123")
|
||||||
|
|
||||||
functions_mapping["length_equals"]("123", 3)
|
functions_mapping["length_equals"]("123", 3)
|
||||||
# Because the Numbers in a CSV file are by default treated as strings,
|
# Because the Numbers in a CSV file are by default treated as strings,
|
||||||
# you need to convert them to Numbers, and we'll test that out here.
|
# you need to convert them to Numbers, and we'll test that out here.
|
||||||
functions_mapping["length_equals"]("123", '3')
|
functions_mapping["length_equals"]("123", "3")
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
functions_mapping["length_equals"]("123", 'abc')
|
functions_mapping["length_equals"]("123", "abc")
|
||||||
functions_mapping["length_greater_than"]("123", 2)
|
functions_mapping["length_greater_than"]("123", 2)
|
||||||
functions_mapping["length_greater_than_or_equals"]("123", 3)
|
functions_mapping["length_greater_than_or_equals"]("123", 3)
|
||||||
|
|
||||||
functions_mapping["contains"]("123abc456", "3ab")
|
functions_mapping["contains"]("123abc456", "3ab")
|
||||||
functions_mapping["contains"](['1', '2'], "1")
|
functions_mapping["contains"](["1", "2"], "1")
|
||||||
functions_mapping["contains"]({'a':1, 'b':2}, "a")
|
functions_mapping["contains"]({"a": 1, "b": 2}, "a")
|
||||||
functions_mapping["contained_by"]("3ab", "123abc456")
|
functions_mapping["contained_by"]("3ab", "123abc456")
|
||||||
|
|
||||||
functions_mapping["regex_match"]("123abc456", "^123\w+456$")
|
functions_mapping["regex_match"]("123abc456", "^123\w+456$")
|
||||||
@@ -119,11 +117,8 @@ class TestUtils(ApiServerUnittest):
|
|||||||
"Request": {
|
"Request": {
|
||||||
"url": "http://127.0.0.1:5000",
|
"url": "http://127.0.0.1:5000",
|
||||||
"METHOD": "POST",
|
"METHOD": "POST",
|
||||||
"Headers": {
|
"Headers": {"Accept": "application/json", "User-Agent": "ios/9.3"},
|
||||||
"Accept": "application/json",
|
},
|
||||||
"User-Agent": "ios/9.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
new_dict = utils.lower_test_dict_keys(origin_dict)
|
new_dict = utils.lower_test_dict_keys(origin_dict)
|
||||||
self.assertIn("name", new_dict)
|
self.assertIn("name", new_dict)
|
||||||
@@ -133,10 +128,7 @@ class TestUtils(ApiServerUnittest):
|
|||||||
self.assertIn("Accept", new_dict["request"]["headers"])
|
self.assertIn("Accept", new_dict["request"]["headers"])
|
||||||
self.assertIn("User-Agent", new_dict["request"]["headers"])
|
self.assertIn("User-Agent", new_dict["request"]["headers"])
|
||||||
|
|
||||||
origin_dict = {
|
origin_dict = {"Name": "test", "Request": "$default_request"}
|
||||||
"Name": "test",
|
|
||||||
"Request": "$default_request"
|
|
||||||
}
|
|
||||||
new_dict = utils.lower_test_dict_keys(origin_dict)
|
new_dict = utils.lower_test_dict_keys(origin_dict)
|
||||||
self.assertIn("$default_request", new_dict["request"])
|
self.assertIn("$default_request", new_dict["request"])
|
||||||
|
|
||||||
@@ -144,10 +136,7 @@ class TestUtils(ApiServerUnittest):
|
|||||||
request_dict = {
|
request_dict = {
|
||||||
"url": "http://127.0.0.1:5000",
|
"url": "http://127.0.0.1:5000",
|
||||||
"METHOD": "POST",
|
"METHOD": "POST",
|
||||||
"Headers": {
|
"Headers": {"Accept": "application/json", "User-Agent": "ios/9.3"},
|
||||||
"Accept": "application/json",
|
|
||||||
"User-Agent": "ios/9.3"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
new_request_dict = utils.lower_dict_keys(request_dict)
|
new_request_dict = utils.lower_dict_keys(request_dict)
|
||||||
self.assertIn("method", new_request_dict)
|
self.assertIn("method", new_request_dict)
|
||||||
@@ -164,10 +153,7 @@ class TestUtils(ApiServerUnittest):
|
|||||||
self.assertEqual(None, request_dict)
|
self.assertEqual(None, request_dict)
|
||||||
|
|
||||||
def test_ensure_mapping_format(self):
|
def test_ensure_mapping_format(self):
|
||||||
map_list = [
|
map_list = [{"a": 1}, {"b": 2}]
|
||||||
{"a": 1},
|
|
||||||
{"b": 2}
|
|
||||||
]
|
|
||||||
ordered_dict = utils.ensure_mapping_format(map_list)
|
ordered_dict = utils.ensure_mapping_format(map_list)
|
||||||
self.assertIsInstance(ordered_dict, dict)
|
self.assertIsInstance(ordered_dict, dict)
|
||||||
self.assertIn("a", ordered_dict)
|
self.assertIn("a", ordered_dict)
|
||||||
@@ -175,7 +161,9 @@ class TestUtils(ApiServerUnittest):
|
|||||||
def test_extend_variables(self):
|
def test_extend_variables(self):
|
||||||
raw_variables = [{"var1": "val1"}, {"var2": "val2"}]
|
raw_variables = [{"var1": "val1"}, {"var2": "val2"}]
|
||||||
override_variables = [{"var1": "val111"}, {"var3": "val3"}]
|
override_variables = [{"var1": "val111"}, {"var3": "val3"}]
|
||||||
extended_variables_mapping = utils.extend_variables(raw_variables, override_variables)
|
extended_variables_mapping = utils.extend_variables(
|
||||||
|
raw_variables, override_variables
|
||||||
|
)
|
||||||
self.assertEqual(extended_variables_mapping["var1"], "val111")
|
self.assertEqual(extended_variables_mapping["var1"], "val111")
|
||||||
self.assertEqual(extended_variables_mapping["var2"], "val2")
|
self.assertEqual(extended_variables_mapping["var2"], "val2")
|
||||||
self.assertEqual(extended_variables_mapping["var3"], "val3")
|
self.assertEqual(extended_variables_mapping["var3"], "val3")
|
||||||
@@ -183,25 +171,27 @@ class TestUtils(ApiServerUnittest):
|
|||||||
def test_extend_variables_fix(self):
|
def test_extend_variables_fix(self):
|
||||||
raw_variables = [{"var1": "val1"}, {"var2": "val2"}]
|
raw_variables = [{"var1": "val1"}, {"var2": "val2"}]
|
||||||
override_variables = {}
|
override_variables = {}
|
||||||
extended_variables_mapping = utils.extend_variables(raw_variables, override_variables)
|
extended_variables_mapping = utils.extend_variables(
|
||||||
|
raw_variables, override_variables
|
||||||
|
)
|
||||||
self.assertEqual(extended_variables_mapping["var1"], "val1")
|
self.assertEqual(extended_variables_mapping["var1"], "val1")
|
||||||
|
|
||||||
def test_deepcopy_dict(self):
|
def test_deepcopy_dict(self):
|
||||||
data = {
|
data = {
|
||||||
'a': 1,
|
"a": 1,
|
||||||
'b': [2, 4],
|
"b": [2, 4],
|
||||||
'c': lambda x: x+1,
|
"c": lambda x: x + 1,
|
||||||
'd': open('LICENSE'),
|
"d": open("LICENSE"),
|
||||||
'f': {
|
"f": {
|
||||||
'f1': {'a1': 2},
|
"f1": {"a1": 2},
|
||||||
'f2': io.open('LICENSE', 'rb'),
|
"f2": io.open("LICENSE", "rb"),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
new_data = utils.deepcopy_dict(data)
|
new_data = utils.deepcopy_dict(data)
|
||||||
data["a"] = 0
|
data["a"] = 0
|
||||||
self.assertEqual(new_data["a"], 1)
|
self.assertEqual(new_data["a"], 1)
|
||||||
data["f"]["f1"] = 123
|
data["f"]["f1"] = 123
|
||||||
self.assertEqual(new_data["f"]["f1"], {'a1': 2})
|
self.assertEqual(new_data["f"]["f1"], {"a1": 2})
|
||||||
self.assertNotEqual(id(new_data["b"]), id(data["b"]))
|
self.assertNotEqual(id(new_data["b"]), id(data["b"]))
|
||||||
self.assertEqual(id(new_data["c"]), id(data["c"]))
|
self.assertEqual(id(new_data["c"]), id(data["c"]))
|
||||||
# self.assertEqual(id(new_data["d"]), id(data["d"]))
|
# self.assertEqual(id(new_data["d"]), id(data["d"]))
|
||||||
@@ -218,41 +208,24 @@ class TestUtils(ApiServerUnittest):
|
|||||||
shutil.rmtree(project_name)
|
shutil.rmtree(project_name)
|
||||||
|
|
||||||
def test_cartesian_product_one(self):
|
def test_cartesian_product_one(self):
|
||||||
parameters_content_list = [
|
parameters_content_list = [[{"a": 1}, {"a": 2}]]
|
||||||
[
|
|
||||||
{"a": 1},
|
|
||||||
{"a": 2}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
product_list = utils.gen_cartesian_product(*parameters_content_list)
|
product_list = utils.gen_cartesian_product(*parameters_content_list)
|
||||||
self.assertEqual(
|
self.assertEqual(product_list, [{"a": 1}, {"a": 2}])
|
||||||
product_list,
|
|
||||||
[
|
|
||||||
{"a": 1},
|
|
||||||
{"a": 2}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_cartesian_product_multiple(self):
|
def test_cartesian_product_multiple(self):
|
||||||
parameters_content_list = [
|
parameters_content_list = [
|
||||||
[
|
[{"a": 1}, {"a": 2}],
|
||||||
{"a": 1},
|
[{"x": 111, "y": 112}, {"x": 121, "y": 122}],
|
||||||
{"a": 2}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{"x": 111, "y": 112},
|
|
||||||
{"x": 121, "y": 122}
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
product_list = utils.gen_cartesian_product(*parameters_content_list)
|
product_list = utils.gen_cartesian_product(*parameters_content_list)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
product_list,
|
product_list,
|
||||||
[
|
[
|
||||||
{'a': 1, 'x': 111, 'y': 112},
|
{"a": 1, "x": 111, "y": 112},
|
||||||
{'a': 1, 'x': 121, 'y': 122},
|
{"a": 1, "x": 121, "y": 122},
|
||||||
{'a': 2, 'x': 111, 'y': 112},
|
{"a": 2, "x": 111, "y": 112},
|
||||||
{'a': 2, 'x': 121, 'y': 122}
|
{"a": 2, "x": 121, "y": 122},
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_cartesian_product_empty(self):
|
def test_cartesian_product_empty(self):
|
||||||
@@ -261,15 +234,7 @@ class TestUtils(ApiServerUnittest):
|
|||||||
self.assertEqual(product_list, [])
|
self.assertEqual(product_list, [])
|
||||||
|
|
||||||
def test_print_info(self):
|
def test_print_info(self):
|
||||||
info_mapping = {
|
info_mapping = {"a": 1, "t": (1, 2), "b": {"b1": 123}, "c": None, "d": [4, 5]}
|
||||||
"a": 1,
|
|
||||||
"t": (1, 2),
|
|
||||||
"b": {
|
|
||||||
"b1": 123
|
|
||||||
},
|
|
||||||
"c": None,
|
|
||||||
"d": [4, 5]
|
|
||||||
}
|
|
||||||
utils.print_info(info_mapping)
|
utils.print_info(info_mapping)
|
||||||
|
|
||||||
def test_prepare_dump_json_file_path_for_folder(self):
|
def test_prepare_dump_json_file_path_for_folder(self):
|
||||||
@@ -277,11 +242,13 @@ class TestUtils(ApiServerUnittest):
|
|||||||
project_working_directory = os.path.join(os.getcwd(), "tests")
|
project_working_directory = os.path.join(os.getcwd(), "tests")
|
||||||
project_mapping = {
|
project_mapping = {
|
||||||
"PWD": project_working_directory,
|
"PWD": project_working_directory,
|
||||||
"test_path": os.path.join(os.getcwd(), "tests", "httpbin", "a.b.c")
|
"test_path": os.path.join(os.getcwd(), "tests", "httpbin", "a.b.c"),
|
||||||
}
|
}
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.prepare_dump_json_file_abs_path(project_mapping, "loaded"),
|
utils.prepare_dump_json_file_abs_path(project_mapping, "loaded"),
|
||||||
os.path.join(project_working_directory, "logs", "httpbin/a.b.c/all.loaded.json")
|
os.path.join(
|
||||||
|
project_working_directory, "logs", "httpbin/a.b.c/all.loaded.json"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_prepare_dump_json_file_path_for_file(self):
|
def test_prepare_dump_json_file_path_for_file(self):
|
||||||
@@ -289,19 +256,23 @@ class TestUtils(ApiServerUnittest):
|
|||||||
project_working_directory = os.path.join(os.getcwd(), "tests")
|
project_working_directory = os.path.join(os.getcwd(), "tests")
|
||||||
project_mapping = {
|
project_mapping = {
|
||||||
"PWD": project_working_directory,
|
"PWD": project_working_directory,
|
||||||
"test_path": os.path.join(os.getcwd(), "tests", "httpbin", "a.b.c", "rpc.yml")
|
"test_path": os.path.join(
|
||||||
|
os.getcwd(), "tests", "httpbin", "a.b.c", "rpc.yml"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.prepare_dump_json_file_abs_path(project_mapping, "loaded"),
|
utils.prepare_dump_json_file_abs_path(project_mapping, "loaded"),
|
||||||
os.path.join(project_working_directory, "logs", "httpbin/a.b.c/rpc.loaded.json")
|
os.path.join(
|
||||||
|
project_working_directory, "logs", "httpbin/a.b.c/rpc.loaded.json"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_prepare_dump_json_file_path_for_passed_testcase(self):
|
def test_prepare_dump_json_file_path_for_passed_testcase(self):
|
||||||
project_working_directory = os.path.join(os.getcwd(), "tests")
|
project_working_directory = os.path.join(os.getcwd(), "tests")
|
||||||
project_mapping = {
|
project_mapping = {"PWD": project_working_directory}
|
||||||
"PWD": project_working_directory
|
|
||||||
}
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.prepare_dump_json_file_abs_path(project_mapping, "loaded"),
|
utils.prepare_dump_json_file_abs_path(project_mapping, "loaded"),
|
||||||
os.path.join(project_working_directory, "logs", "tests_mapping.loaded.json")
|
os.path.join(
|
||||||
|
project_working_directory, "logs", "tests_mapping.loaded.json"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user