mirror of
https://github.com/httprunner/httprunner.git
synced 2026-08-11 08:43:30 +08:00
refactor: format code with black
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
- fix #1217: reload debugtalk.py if loaded
|
||||
- fix #1246: catch exceptions caused by GA report failure
|
||||
- refactor: format code with black
|
||||
|
||||
## 2.5.8 (2022-03-23)
|
||||
|
||||
|
||||
@@ -37,42 +37,37 @@ token_dict = {}
|
||||
|
||||
|
||||
def gen_random_string(str_len):
|
||||
""" generate random string with specified length
|
||||
"""
|
||||
return ''.join(
|
||||
random.choice(string.ascii_letters + string.digits) for _ in range(str_len))
|
||||
"""generate random string with specified length"""
|
||||
return "".join(
|
||||
random.choice(string.ascii_letters + string.digits) for _ in range(str_len)
|
||||
)
|
||||
|
||||
|
||||
def get_sign(*args):
|
||||
content = ''.join(args).encode('ascii')
|
||||
sign_key = SECRET_KEY.encode('ascii')
|
||||
content = "".join(args).encode("ascii")
|
||||
sign_key = SECRET_KEY.encode("ascii")
|
||||
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
||||
return sign
|
||||
|
||||
|
||||
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):
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
device_sn = request.headers.get('device_sn', "")
|
||||
token = request.headers.get('token', "")
|
||||
device_sn = request.headers.get("device_sn", "")
|
||||
token = request.headers.get("token", "")
|
||||
|
||||
if not device_sn or not token:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "device_sn or token is null."
|
||||
}
|
||||
result = {"success": False, "msg": "device_sn or token is null."}
|
||||
response = make_response(json.dumps(result), 401)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
if token_dict.get(device_sn) != token:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "Authorization failed!"
|
||||
}
|
||||
result = {"success": False, "msg": "Authorization failed!"}
|
||||
response = make_response(json.dumps(result), 403)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
@@ -82,107 +77,89 @@ def validate_request(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
@app.route('/')
|
||||
@app.route("/")
|
||||
def index():
|
||||
return "Hello World!"
|
||||
|
||||
@app.route('/api/get-token', methods=['POST'])
|
||||
|
||||
@app.route("/api/get-token", methods=["POST"])
|
||||
def get_token():
|
||||
device_sn = request.headers.get('device_sn', "")
|
||||
os_platform = request.headers.get('os_platform', "")
|
||||
app_version = request.headers.get('app_version', "")
|
||||
device_sn = request.headers.get("device_sn", "")
|
||||
os_platform = request.headers.get("os_platform", "")
|
||||
app_version = request.headers.get("app_version", "")
|
||||
data = request.get_json()
|
||||
sign = data.get('sign', "")
|
||||
sign = data.get("sign", "")
|
||||
|
||||
expected_sign = get_sign(device_sn, os_platform, app_version)
|
||||
|
||||
if expected_sign != sign:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "Authorization failed!"
|
||||
}
|
||||
result = {"success": False, "msg": "Authorization failed!"}
|
||||
response = make_response(json.dumps(result), 403)
|
||||
else:
|
||||
token = gen_random_string(16)
|
||||
token_dict[device_sn] = token
|
||||
|
||||
result = {
|
||||
'success': True,
|
||||
'token': token
|
||||
}
|
||||
result = {"success": True, "token": token}
|
||||
response = make_response(json.dumps(result))
|
||||
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@app.route('/api/users')
|
||||
|
||||
@app.route("/api/users")
|
||||
@validate_request
|
||||
def get_users():
|
||||
users_list = [user for uid, user in users_dict.items()]
|
||||
users = {
|
||||
'success': True,
|
||||
'count': len(users_list),
|
||||
'items': users_list
|
||||
}
|
||||
users = {"success": True, "count": len(users_list), "items": users_list}
|
||||
response = make_response(json.dumps(users))
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@app.route('/api/reset-all')
|
||||
|
||||
@app.route("/api/reset-all")
|
||||
@validate_request
|
||||
def clear_users():
|
||||
users_dict.clear()
|
||||
result = {
|
||||
'success': True
|
||||
}
|
||||
result = {"success": True}
|
||||
response = make_response(json.dumps(result))
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@app.route('/api/users/<int:uid>', methods=['POST'])
|
||||
|
||||
@app.route("/api/users/<int:uid>", methods=["POST"])
|
||||
@validate_request
|
||||
def create_user(uid):
|
||||
user = request.get_json()
|
||||
if uid not in users_dict:
|
||||
result = {
|
||||
'success': True,
|
||||
'msg': "user created successfully."
|
||||
}
|
||||
result = {"success": True, "msg": "user created successfully."}
|
||||
status_code = 201
|
||||
users_dict[uid] = user
|
||||
else:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "user already existed."
|
||||
}
|
||||
result = {"success": False, "msg": "user already existed."}
|
||||
status_code = 500
|
||||
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@app.route('/api/users/<int:uid>')
|
||||
|
||||
@app.route("/api/users/<int:uid>")
|
||||
@validate_request
|
||||
def get_user(uid):
|
||||
user = users_dict.get(uid, {})
|
||||
if user:
|
||||
result = {
|
||||
'success': True,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": True, "data": user}
|
||||
status_code = 200
|
||||
else:
|
||||
result = {
|
||||
'success': False,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": False, "data": user}
|
||||
status_code = 404
|
||||
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@app.route('/api/users/<int:uid>', methods=['PUT'])
|
||||
|
||||
@app.route("/api/users/<int:uid>", methods=["PUT"])
|
||||
@validate_request
|
||||
def update_user(uid):
|
||||
user = users_dict.get(uid, {})
|
||||
@@ -195,15 +172,13 @@ def update_user(uid):
|
||||
success = False
|
||||
status_code = 404
|
||||
|
||||
result = {
|
||||
'success': success,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": success, "data": user}
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@app.route('/api/users/<int:uid>', methods=['DELETE'])
|
||||
|
||||
@app.route("/api/users/<int:uid>", methods=["DELETE"])
|
||||
@validate_request
|
||||
def delete_user(uid):
|
||||
user = users_dict.pop(uid, {})
|
||||
@@ -214,10 +189,7 @@ def delete_user(uid):
|
||||
success = False
|
||||
status_code = 404
|
||||
|
||||
result = {
|
||||
'success': success,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": success, "data": user}
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@@ -6,43 +6,41 @@ import time
|
||||
|
||||
SECRET_KEY = "DebugTalk"
|
||||
|
||||
|
||||
def gen_random_string(str_len):
|
||||
random_char_list = []
|
||||
for _ in range(str_len):
|
||||
random_char = random.choice(string.ascii_letters + string.digits)
|
||||
random_char_list.append(random_char)
|
||||
|
||||
random_string = ''.join(random_char_list)
|
||||
random_string = "".join(random_char_list)
|
||||
return random_string
|
||||
|
||||
|
||||
def get_sign(*args):
|
||||
content = ''.join(args).encode('ascii')
|
||||
sign_key = SECRET_KEY.encode('ascii')
|
||||
content = "".join(args).encode("ascii")
|
||||
sign_key = SECRET_KEY.encode("ascii")
|
||||
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
||||
return sign
|
||||
|
||||
|
||||
def gen_user_id():
|
||||
return int(time.time() * 1000)
|
||||
|
||||
|
||||
def get_user_id():
|
||||
return [
|
||||
{"user_id": 1001},
|
||||
{"user_id": 1002},
|
||||
{"user_id": 1003},
|
||||
{"user_id": 1004}
|
||||
]
|
||||
return [{"user_id": 1001}, {"user_id": 1002}, {"user_id": 1003}, {"user_id": 1004}]
|
||||
|
||||
|
||||
def get_account(num):
|
||||
accounts = []
|
||||
for index in range(1, num+1):
|
||||
for index in range(1, num + 1):
|
||||
accounts.append(
|
||||
{"username": "user%s" % index, "password": str(index) * 6},
|
||||
)
|
||||
|
||||
return accounts
|
||||
|
||||
|
||||
def get_os_platform():
|
||||
return [
|
||||
{"os_platform": "ios"},
|
||||
{"os_platform": "android"}
|
||||
]
|
||||
return [{"os_platform": "ios"}, {"os_platform": "android"}]
|
||||
|
||||
@@ -7,5 +7,5 @@ __all__ = [
|
||||
klook_len_eq,
|
||||
check_search_area_result,
|
||||
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)
|
||||
found = False
|
||||
for item in content:
|
||||
if item['fullName'] == expect_name:
|
||||
if item["fullName"] == expect_name:
|
||||
found = True
|
||||
break
|
||||
assert found
|
||||
@@ -11,8 +11,12 @@ def check_search_area_result(content, expect_name):
|
||||
def exists_default_group(content, expect):
|
||||
found = False
|
||||
for item in content:
|
||||
if item['defaultGroup']:
|
||||
print('defaultGroup found, id={}, parentAreaId={}'.format(item['id'], item['parentAreaId']))
|
||||
if item["defaultGroup"]:
|
||||
print(
|
||||
"defaultGroup found, id={}, parentAreaId={}".format(
|
||||
item["id"], item["parentAreaId"]
|
||||
)
|
||||
)
|
||||
found = True
|
||||
break
|
||||
assert found
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from httprunner import (__version__, exceptions, loader, logger, parser,
|
||||
report, runner, utils)
|
||||
from httprunner import (
|
||||
__version__,
|
||||
exceptions,
|
||||
loader,
|
||||
logger,
|
||||
parser,
|
||||
report,
|
||||
runner,
|
||||
utils,
|
||||
)
|
||||
from httprunner.utils import ga_client
|
||||
|
||||
|
||||
class HttpRunner(object):
|
||||
""" Developer Interface: Main Interface
|
||||
Usage:
|
||||
"""Developer Interface: Main Interface
|
||||
Usage:
|
||||
|
||||
from httprunner.api import HttpRunner
|
||||
runner = HttpRunner(
|
||||
failfast=True,
|
||||
save_tests=True,
|
||||
log_level="INFO",
|
||||
log_file="test.log"
|
||||
)
|
||||
summary = runner.run(path_or_tests)
|
||||
from httprunner.api import HttpRunner
|
||||
runner = HttpRunner(
|
||||
failfast=True,
|
||||
save_tests=True,
|
||||
log_level="INFO",
|
||||
log_file="test.log"
|
||||
)
|
||||
summary = runner.run(path_or_tests)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, failfast=False, save_tests=False, log_level="WARNING", log_file=None):
|
||||
""" initialize HttpRunner.
|
||||
def __init__(
|
||||
self, failfast=False, save_tests=False, log_level="WARNING", log_file=None
|
||||
):
|
||||
"""initialize HttpRunner.
|
||||
|
||||
Args:
|
||||
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)
|
||||
|
||||
self.exception_stage = "initialize HttpRunner()"
|
||||
kwargs = {
|
||||
"failfast": failfast,
|
||||
"resultclass": report.HtmlTestResult
|
||||
}
|
||||
kwargs = {"failfast": failfast, "resultclass": report.HtmlTestResult}
|
||||
self.unittest_runner = unittest.TextTestRunner(**kwargs)
|
||||
self.test_loader = unittest.TestLoader()
|
||||
self.save_tests = save_tests
|
||||
@@ -45,7 +52,7 @@ class HttpRunner(object):
|
||||
self.project_working_directory = None
|
||||
|
||||
def _add_tests(self, testcases):
|
||||
""" initialize testcase with Runner() and add to test suite.
|
||||
"""initialize testcase with Runner() and add to test suite.
|
||||
|
||||
Args:
|
||||
testcases (list): testcases list.
|
||||
@@ -54,9 +61,10 @@ class HttpRunner(object):
|
||||
unittest.TestSuite()
|
||||
|
||||
"""
|
||||
|
||||
def _add_test(test_runner, test_dict):
|
||||
""" add test to testcase.
|
||||
"""
|
||||
"""add test to testcase."""
|
||||
|
||||
def test(self):
|
||||
try:
|
||||
test_runner.run_test(test_dict)
|
||||
@@ -89,7 +97,7 @@ class HttpRunner(object):
|
||||
for testcase in testcases:
|
||||
config = testcase.get("config", {})
|
||||
test_runner = runner.Runner(config)
|
||||
TestSequense = type('TestSequense', (unittest.TestCase,), {})
|
||||
TestSequense = type("TestSequense", (unittest.TestCase,), {})
|
||||
|
||||
tests = testcase.get("teststeps", [])
|
||||
for index, test_dict in enumerate(tests):
|
||||
@@ -98,12 +106,13 @@ class HttpRunner(object):
|
||||
times = int(times)
|
||||
except ValueError:
|
||||
raise exceptions.ParamsError(
|
||||
"times should be digit, given: {}".format(times))
|
||||
"times should be digit, given: {}".format(times)
|
||||
)
|
||||
|
||||
for times_index in range(times):
|
||||
# suppose one testcase should not have more than 9999 steps,
|
||||
# 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)
|
||||
setattr(TestSequense, test_method_name, test_method)
|
||||
|
||||
@@ -116,7 +125,7 @@ class HttpRunner(object):
|
||||
return test_suite
|
||||
|
||||
def _run_suite(self, test_suite):
|
||||
""" run tests in test_suite
|
||||
"""run tests in test_suite
|
||||
|
||||
Args:
|
||||
test_suite: unittest.TestSuite()
|
||||
@@ -140,7 +149,7 @@ class HttpRunner(object):
|
||||
return tests_results
|
||||
|
||||
def _aggregate(self, tests_results):
|
||||
""" aggregate results
|
||||
"""aggregate results
|
||||
|
||||
Args:
|
||||
tests_results (list): list of (testcase, result)
|
||||
@@ -149,16 +158,12 @@ class HttpRunner(object):
|
||||
summary = {
|
||||
"success": True,
|
||||
"stat": {
|
||||
"testcases": {
|
||||
"total": len(tests_results),
|
||||
"success": 0,
|
||||
"fail": 0
|
||||
},
|
||||
"teststeps": {}
|
||||
"testcases": {"total": len(tests_results), "success": 0, "fail": 0},
|
||||
"teststeps": {},
|
||||
},
|
||||
"time": {},
|
||||
"platform": report.get_platform(),
|
||||
"details": []
|
||||
"details": [],
|
||||
}
|
||||
|
||||
for tests_result in tests_results:
|
||||
@@ -174,7 +179,9 @@ class HttpRunner(object):
|
||||
testcase_summary["name"] = testcase.config.get("name")
|
||||
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"])
|
||||
|
||||
summary["details"].append(testcase_summary)
|
||||
@@ -182,8 +189,7 @@ class HttpRunner(object):
|
||||
return summary
|
||||
|
||||
def run_tests(self, tests_mapping):
|
||||
""" run testcase/testsuite data
|
||||
"""
|
||||
"""run testcase/testsuite data"""
|
||||
ga_client.track_event("RunAPITests", "hrun")
|
||||
project_mapping = tests_mapping.get("project_mapping", {})
|
||||
self.project_working_directory = project_mapping.get("PWD", os.getcwd())
|
||||
@@ -231,7 +237,7 @@ class HttpRunner(object):
|
||||
return self._summary
|
||||
|
||||
def get_vars_out(self):
|
||||
""" get variables and output
|
||||
"""get variables and output
|
||||
Returns:
|
||||
list: list of variables and output.
|
||||
if tests are parameterized, list items are corresponded to parameters.
|
||||
@@ -254,13 +260,10 @@ class HttpRunner(object):
|
||||
if not self._summary:
|
||||
return None
|
||||
|
||||
return [
|
||||
summary["in_out"]
|
||||
for summary in self._summary["details"]
|
||||
]
|
||||
return [summary["in_out"] for summary in self._summary["details"]]
|
||||
|
||||
def run_path(self, path, dot_env_path=None, mapping=None):
|
||||
""" run testcase/testsuite file or folder.
|
||||
"""run testcase/testsuite file or folder.
|
||||
|
||||
Args:
|
||||
path (str): testcase/testsuite file/foler path.
|
||||
@@ -281,7 +284,7 @@ class HttpRunner(object):
|
||||
return self.run_tests(tests_mapping)
|
||||
|
||||
def run(self, path_or_tests, dot_env_path=None, mapping=None):
|
||||
""" main interface.
|
||||
"""main interface.
|
||||
|
||||
Args:
|
||||
path_or_tests:
|
||||
@@ -298,8 +301,12 @@ class HttpRunner(object):
|
||||
if loader.is_test_path(path_or_tests):
|
||||
return self.run_path(path_or_tests, dot_env_path, mapping)
|
||||
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)
|
||||
return self.run_tests(path_or_tests)
|
||||
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:
|
||||
return int(expect_value)
|
||||
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):
|
||||
""" generate random string with specified length
|
||||
"""
|
||||
return ''.join(
|
||||
random.choice(string.ascii_letters + string.digits) for _ in range(str_len))
|
||||
"""generate random string with specified length"""
|
||||
return "".join(
|
||||
random.choice(string.ascii_letters + string.digits) for _ in range(str_len)
|
||||
)
|
||||
|
||||
|
||||
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:
|
||||
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"):
|
||||
""" get current date, default format is %Y-%m-%d
|
||||
"""
|
||||
"""get current date, default format is %Y-%m-%d"""
|
||||
return datetime.datetime.now().strftime(fmt)
|
||||
|
||||
|
||||
def sleep(n_secs):
|
||||
""" sleep n seconds
|
||||
"""
|
||||
"""sleep n seconds"""
|
||||
time.sleep(n_secs)
|
||||
|
||||
|
||||
@@ -10,58 +10,63 @@ from httprunner.compat import is_py2
|
||||
from httprunner.loader import load_cases
|
||||
from httprunner.logger import color_print, log_error
|
||||
from httprunner.report import gen_html_report
|
||||
from httprunner.utils import (create_scaffold, get_python2_retire_msg,
|
||||
prettify_json_file, init_sentry_sdk)
|
||||
from httprunner.utils import (
|
||||
create_scaffold,
|
||||
get_python2_retire_msg,
|
||||
prettify_json_file,
|
||||
init_sentry_sdk,
|
||||
)
|
||||
|
||||
init_sentry_sdk()
|
||||
|
||||
|
||||
def main():
|
||||
""" API test: parse command line options and run commands.
|
||||
"""
|
||||
"""API test: parse command line options and run commands."""
|
||||
if is_py2:
|
||||
color_print(get_python2_retire_msg(), "YELLOW")
|
||||
|
||||
parser = argparse.ArgumentParser(description=__description__)
|
||||
parser.add_argument(
|
||||
'-V', '--version', dest='version', action='store_true',
|
||||
help="show version")
|
||||
"-V", "--version", dest="version", action="store_true", help="show version"
|
||||
)
|
||||
parser.add_argument(
|
||||
'testfile_paths', nargs='*',
|
||||
help="Specify api/testcase/testsuite file paths to run.")
|
||||
"testfile_paths",
|
||||
nargs="*",
|
||||
help="Specify api/testcase/testsuite file paths to run.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--log-level', default='INFO',
|
||||
help="Specify logging level, default is INFO.")
|
||||
"--log-level", default="INFO", help="Specify logging level, default is INFO."
|
||||
)
|
||||
parser.add_argument("--log-file", help="Write logs to specified file path.")
|
||||
parser.add_argument(
|
||||
'--log-file',
|
||||
help="Write logs to specified file path.")
|
||||
"--dot-env-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(
|
||||
'--dot-env-path',
|
||||
help="Specify .env file path, which is useful for keeping sensitive data.")
|
||||
"--report-file",
|
||||
help="Specify report file path, this has higher priority than specifying report dir.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--report-template',
|
||||
help="Specify report template path.")
|
||||
"--save-tests",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Save loaded/parsed/vars_out/summary json data to JSON files.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--report-dir',
|
||||
help="Specify report save directory.")
|
||||
"--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(
|
||||
'--report-file',
|
||||
help="Specify report file path, this has higher priority than specifying report dir.")
|
||||
parser.add_argument(
|
||||
'--save-tests', action='store_true', default=False,
|
||||
help="Save loaded/parsed/vars_out/summary json data to JSON files.")
|
||||
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.")
|
||||
"--validate",
|
||||
nargs="*",
|
||||
help="Validate YAML/JSON api/testcase/testsuite format.",
|
||||
)
|
||||
parser.add_argument("--prettify", nargs="*", help="Prettify JSON testcase format.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -99,23 +104,28 @@ def main():
|
||||
failfast=args.failfast,
|
||||
save_tests=args.save_tests,
|
||||
log_level=args.log_level,
|
||||
log_file=args.log_file
|
||||
log_file=args.log_file,
|
||||
)
|
||||
|
||||
err_code = 0
|
||||
try:
|
||||
for path in args.testfile_paths:
|
||||
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(
|
||||
summary,
|
||||
report_template=args.report_template,
|
||||
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:
|
||||
color_print("!!!!!!!!!! exception stage: {} !!!!!!!!!!".format(runner.exception_stage), "YELLOW")
|
||||
color_print(
|
||||
"!!!!!!!!!! exception stage: {} !!!!!!!!!!".format(runner.exception_stage),
|
||||
"YELLOW",
|
||||
)
|
||||
color_print(str(ex), "RED")
|
||||
sentry_sdk.capture_exception(ex)
|
||||
err_code = 1
|
||||
@@ -123,5 +133,5 @@ def main():
|
||||
sys.exit(err_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -5,8 +5,12 @@ import time
|
||||
import requests
|
||||
import urllib3
|
||||
from requests import Request, Response
|
||||
from requests.exceptions import (InvalidSchema, InvalidURL, MissingSchema,
|
||||
RequestException)
|
||||
from requests.exceptions import (
|
||||
InvalidSchema,
|
||||
InvalidURL,
|
||||
MissingSchema,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
from httprunner import logger, response
|
||||
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):
|
||||
""" get request and response info from Response() object.
|
||||
"""
|
||||
"""get request and response info from Response() object."""
|
||||
|
||||
def log_print(req_resp_dict, r_type):
|
||||
msg = "\n================== {} details ==================\n".format(r_type)
|
||||
for key, value in req_resp_dict[r_type].items():
|
||||
msg += "{:<16} : {}\n".format(key, repr(value))
|
||||
logger.log_debug(msg)
|
||||
|
||||
req_resp_dict = {
|
||||
"request": {},
|
||||
"response": {}
|
||||
}
|
||||
req_resp_dict = {"request": {}, "response": {}}
|
||||
|
||||
# record actual request info
|
||||
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
|
||||
if request_body:
|
||||
request_content_type = lower_dict_keys(
|
||||
req_resp_dict["request"]["headers"]
|
||||
).get("content-type")
|
||||
request_content_type = lower_dict_keys(req_resp_dict["request"]["headers"]).get(
|
||||
"content-type"
|
||||
)
|
||||
if request_content_type and "multipart/form-data" in request_content_type:
|
||||
# upload file type
|
||||
req_resp_dict["request"]["body"] = "upload file stream (OMITTED)"
|
||||
@@ -83,9 +84,8 @@ def get_req_resp_record(resp_obj):
|
||||
|
||||
|
||||
class ApiResponse(Response):
|
||||
|
||||
def raise_for_status(self):
|
||||
if hasattr(self, 'error') and self.error:
|
||||
if hasattr(self, "error") and self.error:
|
||||
raise self.error
|
||||
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
|
||||
:py:class:`requests.Session` class and mostly this class works exactly the same.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(HttpSession, self).__init__()
|
||||
self.init_meta_data()
|
||||
|
||||
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 = {
|
||||
"name": "",
|
||||
"data": [
|
||||
{
|
||||
"request": {
|
||||
"url": "N/A",
|
||||
"method": "N/A",
|
||||
"headers": {}
|
||||
},
|
||||
"request": {"url": "N/A", "method": "N/A", "headers": {}},
|
||||
"response": {
|
||||
"status_code": "N/A",
|
||||
"headers": {},
|
||||
"encoding": None,
|
||||
"content_type": ""
|
||||
}
|
||||
"content_type": "",
|
||||
},
|
||||
}
|
||||
],
|
||||
"stat": {
|
||||
"content_size": "N/A",
|
||||
"response_time_ms": "N/A",
|
||||
"elapsed_ms": "N/A",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
def update_last_req_resp_record(self, resp_obj):
|
||||
@@ -202,26 +198,23 @@ class HttpSession(requests.Session):
|
||||
self.meta_data["stat"] = {
|
||||
"response_time_ms": response_time_ms,
|
||||
"elapsed_ms": response.elapsed.microseconds / 1000.0,
|
||||
"content_size": content_size
|
||||
"content_size": content_size,
|
||||
}
|
||||
|
||||
# record request and response histories, include 30X redirection
|
||||
response_list = response.history + [response]
|
||||
self.meta_data["data"] = [
|
||||
get_req_resp_record(resp_obj)
|
||||
for resp_obj in response_list
|
||||
get_req_resp_record(resp_obj) for resp_obj in response_list
|
||||
]
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except RequestException as e:
|
||||
logger.log_error(u"{exception}".format(exception=str(e)))
|
||||
logger.log_error("{exception}".format(exception=str(e)))
|
||||
else:
|
||||
logger.log_info(
|
||||
"""status_code: {}, response_time(ms): {} ms, response_length: {} bytes\n""".format(
|
||||
response.status_code,
|
||||
response_time_ms,
|
||||
content_size
|
||||
response.status_code, response_time_ms, content_size
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ import sys
|
||||
_ver = sys.version_info
|
||||
|
||||
#: Python 2.x?
|
||||
is_py2 = (_ver[0] == 2)
|
||||
is_py2 = _ver[0] == 2
|
||||
|
||||
#: 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):
|
||||
""" HttpRunner session, store runtime variables.
|
||||
"""HttpRunner session, store runtime variables.
|
||||
|
||||
Examples:
|
||||
>>> variables = {"SECRET_KEY": "DebugTalk"}
|
||||
@@ -16,12 +16,14 @@ class SessionContext(object):
|
||||
|
||||
def __init__(self, variables=None):
|
||||
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.init_test_variables()
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
@@ -45,20 +47,19 @@ class SessionContext(object):
|
||||
self.test_variables_mapping.update(self.session_variables_mapping)
|
||||
|
||||
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
|
||||
|
||||
def update_session_variables(self, variables_mapping):
|
||||
""" update session with extracted variables mapping.
|
||||
these variables are valid in the whole running session.
|
||||
"""update session with extracted variables mapping.
|
||||
these variables are valid in the whole running session.
|
||||
"""
|
||||
variables_mapping = utils.ensure_mapping_format(variables_mapping)
|
||||
self.session_variables_mapping.update(variables_mapping)
|
||||
self.test_variables_mapping.update(self.session_variables_mapping)
|
||||
|
||||
def eval_content(self, 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.
|
||||
"""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.
|
||||
"""
|
||||
return parser.parse_lazy_data(content, self.test_variables_mapping)
|
||||
|
||||
@@ -81,5 +81,4 @@ class TestcaseNotFound(NotFoundError):
|
||||
|
||||
|
||||
class SummaryEmpty(MyBaseError):
|
||||
""" test result summary data is empty
|
||||
"""
|
||||
"""test result summary data is empty"""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
try:
|
||||
# monkey patch ssl at beginning to avoid RecursionError when running locust.
|
||||
from gevent import monkey
|
||||
|
||||
monkey.patch_ssl()
|
||||
from locust import main as locust_main
|
||||
except ImportError:
|
||||
@@ -11,6 +12,7 @@ $ pip install locustio
|
||||
"""
|
||||
print(msg)
|
||||
import sys
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
import io
|
||||
@@ -26,9 +28,9 @@ init_sentry_sdk()
|
||||
|
||||
|
||||
def parse_locustfile(file_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 YAML/JSON file, convert it to locustfile
|
||||
"""parse testcase file and return locustfile path.
|
||||
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 not os.path.isfile(file_path):
|
||||
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]
|
||||
if file_suffix == ".py":
|
||||
locustfile_path = file_path
|
||||
elif file_suffix in ['.yaml', '.yml', '.json']:
|
||||
elif file_suffix in [".yaml", ".yml", ".json"]:
|
||||
locustfile_path = gen_locustfile(file_path)
|
||||
else:
|
||||
# '' or other suffix
|
||||
@@ -48,18 +50,18 @@ def parse_locustfile(file_path):
|
||||
|
||||
|
||||
def gen_locustfile(testcase_file_path):
|
||||
""" generate locustfile from template.
|
||||
"""
|
||||
locustfile_path = 'locustfile.py'
|
||||
"""generate locustfile from template."""
|
||||
locustfile_path = "locustfile.py"
|
||||
template_path = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
"locustfile_template.py"
|
||||
os.path.dirname(os.path.realpath(__file__)), "locustfile_template.py"
|
||||
)
|
||||
|
||||
with io.open(template_path, encoding='utf-8') as template:
|
||||
with io.open(locustfile_path, 'w', encoding='utf-8') as locustfile:
|
||||
with io.open(template_path, encoding="utf-8") as template:
|
||||
with io.open(locustfile_path, "w", encoding="utf-8") as locustfile:
|
||||
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)
|
||||
|
||||
return locustfile_path
|
||||
@@ -67,6 +69,7 @@ def gen_locustfile(testcase_file_path):
|
||||
|
||||
def start_locust_main():
|
||||
from httprunner.utils import ga_client
|
||||
|
||||
ga_client.track_event("RunLoadTests", "locust")
|
||||
|
||||
locust_main.main()
|
||||
@@ -106,10 +109,9 @@ def run_locusts_with_processes(sys_argv, processes_count):
|
||||
|
||||
|
||||
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__))
|
||||
sys.argv[0] = 'locust'
|
||||
sys.argv[0] = "locust"
|
||||
if len(sys.argv) == 1:
|
||||
sys.argv.extend(["-h"])
|
||||
|
||||
@@ -147,29 +149,36 @@ def main():
|
||||
sys.argv[testcase_index] = parse_locustfile(testcase_file_path)
|
||||
|
||||
if "--processes" in sys.argv:
|
||||
""" locusts -f locustfile.py --processes 4
|
||||
"""
|
||||
"""locusts -f locustfile.py --processes 4"""
|
||||
if "--no-web" in sys.argv:
|
||||
logger.log_error("conflict parameter args: --processes & --no-web. \nexit.")
|
||||
sys.exit(1)
|
||||
|
||||
processes_index = sys.argv.index('--processes')
|
||||
processes_index = sys.argv.index("--processes")
|
||||
processes_count_index = processes_index + 1
|
||||
if processes_count_index >= len(sys.argv):
|
||||
""" do not specify processes count explicitly
|
||||
locusts -f locustfile.py --processes
|
||||
"""do not specify processes count explicitly
|
||||
locusts -f locustfile.py --processes
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
""" locusts -f locustfile.py --processes 4 """
|
||||
"""locusts -f locustfile.py --processes 4"""
|
||||
processes_count = int(sys.argv[processes_count_index])
|
||||
sys.argv.pop(processes_count_index)
|
||||
except ValueError:
|
||||
""" locusts -f locustfile.py --processes -P 8888 """
|
||||
"""locusts -f locustfile.py --processes -P 8888"""
|
||||
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)
|
||||
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
|
||||
|
||||
logging.getLogger().setLevel(logging.CRITICAL)
|
||||
logging.getLogger('locust.main').setLevel(logging.INFO)
|
||||
logging.getLogger('locust.runners').setLevel(logging.INFO)
|
||||
logging.getLogger("locust.main").setLevel(logging.INFO)
|
||||
logging.getLogger("locust.runners").setLevel(logging.INFO)
|
||||
|
||||
|
||||
class WebPageTasks(TaskSet):
|
||||
@@ -28,7 +28,7 @@ class WebPageTasks(TaskSet):
|
||||
request_type=self.test_runner.exception_request_type,
|
||||
name=self.test_runner.exception_name,
|
||||
response_time=0,
|
||||
exception=ex
|
||||
exception=ex,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from httprunner import loader, parser
|
||||
|
||||
|
||||
def prepare_locust_tests(path):
|
||||
""" prepare locust testcases
|
||||
"""prepare locust testcases
|
||||
|
||||
Args:
|
||||
path (str): testcase file path.
|
||||
|
||||
@@ -61,7 +61,7 @@ from httprunner.exceptions import ParamsError
|
||||
|
||||
|
||||
def prepare_upload_test(test_dict):
|
||||
""" preprocess for upload test
|
||||
"""preprocess for upload test
|
||||
replace `upload` info with MultipartEncoder
|
||||
|
||||
Args:
|
||||
@@ -96,14 +96,15 @@ def prepare_upload_test(test_dict):
|
||||
test_dict["variables"]["m_encoder"] = "${multipart_encoder(" + params_str + ")}"
|
||||
|
||||
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"
|
||||
|
||||
|
||||
def multipart_encoder(**kwargs):
|
||||
""" initialize MultipartEncoder with uploading fields.
|
||||
"""
|
||||
"""initialize MultipartEncoder with uploading fields."""
|
||||
|
||||
def get_filetype(file_path):
|
||||
file_type = filetype.guess(file_path)
|
||||
@@ -122,6 +123,7 @@ def multipart_encoder(**kwargs):
|
||||
else:
|
||||
# value is not absolute file path, check if it is relative file path
|
||||
from httprunner.loader import get_pwd
|
||||
|
||||
_file_path = os.path.join(get_pwd(), value)
|
||||
is_exists_file = os.path.isfile(_file_path)
|
||||
|
||||
@@ -130,7 +132,7 @@ def multipart_encoder(**kwargs):
|
||||
filename = os.path.basename(_file_path)
|
||||
mime_type = get_filetype(_file_path)
|
||||
# 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)
|
||||
else:
|
||||
fields_dict[key] = value
|
||||
@@ -139,6 +141,5 @@ def multipart_encoder(**kwargs):
|
||||
|
||||
|
||||
def multipart_content_type(m_encoder):
|
||||
""" prepare Content-Type for request headers
|
||||
"""
|
||||
"""prepare Content-Type for request headers"""
|
||||
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.locate import get_project_working_directory as get_pwd, \
|
||||
init_project_working_directory as init_pwd
|
||||
from httprunner.loader.locate import (
|
||||
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.buildup import load_cases, load_project_data
|
||||
|
||||
@@ -23,5 +25,5 @@ __all__ = [
|
||||
"load_csv_file",
|
||||
"load_builtin_functions",
|
||||
"load_project_data",
|
||||
"load_cases"
|
||||
"load_cases",
|
||||
]
|
||||
|
||||
@@ -4,18 +4,22 @@ import sys
|
||||
|
||||
from httprunner import exceptions, logger, utils
|
||||
from httprunner.loader.check import JsonSchemaChecker
|
||||
from httprunner.loader.load import load_module_functions, load_file, load_dot_env_file, \
|
||||
load_folder_files
|
||||
from httprunner.loader.locate import init_project_working_directory, get_project_working_directory
|
||||
from httprunner.loader.load import (
|
||||
load_module_functions,
|
||||
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 = {
|
||||
"api": {},
|
||||
"testcases": {}
|
||||
}
|
||||
tests_def_mapping = {"api": {}, "testcases": {}}
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Returns:
|
||||
@@ -27,8 +31,8 @@ def load_debugtalk_functions():
|
||||
|
||||
"""
|
||||
# load debugtalk.py module
|
||||
if sys.modules.get('debugtalk'):
|
||||
imported_module = importlib.reload(sys.modules['debugtalk'])
|
||||
if sys.modules.get("debugtalk"):
|
||||
imported_module = importlib.reload(sys.modules["debugtalk"])
|
||||
else:
|
||||
imported_module = importlib.import_module("debugtalk")
|
||||
|
||||
@@ -36,7 +40,7 @@ def load_debugtalk_functions():
|
||||
|
||||
|
||||
def __extend_with_api_ref(raw_testinfo):
|
||||
""" extend with api reference
|
||||
"""extend with api reference
|
||||
|
||||
Raises:
|
||||
exceptions.ApiNotFound: api not found
|
||||
@@ -68,17 +72,13 @@ def __extend_with_api_ref(raw_testinfo):
|
||||
|
||||
|
||||
def __extend_with_testcase_ref(raw_testinfo):
|
||||
""" extend with testcase reference
|
||||
"""
|
||||
"""extend with testcase reference"""
|
||||
testcase_path = raw_testinfo["testcase"]
|
||||
|
||||
if testcase_path not in tests_def_mapping["testcases"]:
|
||||
# make compatible with Windows/Linux
|
||||
pwd = get_project_working_directory()
|
||||
testcase_path = os.path.join(
|
||||
pwd,
|
||||
*testcase_path.split("/")
|
||||
)
|
||||
testcase_path = os.path.join(pwd, *testcase_path.split("/"))
|
||||
loaded_testcase = load_file(testcase_path)
|
||||
|
||||
if isinstance(loaded_testcase, list):
|
||||
@@ -89,7 +89,8 @@ def __extend_with_testcase_ref(raw_testinfo):
|
||||
testcase_dict = load_testcase_v2(loaded_testcase)
|
||||
else:
|
||||
raise exceptions.FileFormatError(
|
||||
"Invalid format testcase: {}".format(testcase_path))
|
||||
"Invalid format testcase: {}".format(testcase_path)
|
||||
)
|
||||
|
||||
tests_def_mapping["testcases"][testcase_path] = testcase_dict
|
||||
else:
|
||||
@@ -99,7 +100,7 @@ def __extend_with_testcase_ref(raw_testinfo):
|
||||
|
||||
|
||||
def load_teststep(raw_testinfo):
|
||||
""" load testcase step content.
|
||||
"""load testcase step content.
|
||||
teststep maybe defined directly, or reference api/testcase.
|
||||
|
||||
Args:
|
||||
@@ -151,7 +152,7 @@ def load_teststep(raw_testinfo):
|
||||
|
||||
|
||||
def load_testcase(raw_testcase):
|
||||
""" load testcase with api/testcase references.
|
||||
"""load testcase with api/testcase references.
|
||||
|
||||
Args:
|
||||
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))
|
||||
else:
|
||||
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 {
|
||||
"config": config,
|
||||
"teststeps": tests
|
||||
}
|
||||
return {"config": config, "teststeps": tests}
|
||||
|
||||
|
||||
def load_testcase_v2(raw_testcase):
|
||||
""" load testcase in format version 2.
|
||||
"""load testcase in format version 2.
|
||||
|
||||
Args:
|
||||
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)
|
||||
raw_teststeps = raw_testcase.pop("teststeps")
|
||||
raw_testcase["teststeps"] = [
|
||||
load_teststep(teststep)
|
||||
for teststep in raw_teststeps
|
||||
]
|
||||
raw_testcase["teststeps"] = [load_teststep(teststep) for teststep in raw_teststeps]
|
||||
return raw_testcase
|
||||
|
||||
|
||||
def load_testsuite(raw_testsuite):
|
||||
""" load testsuite with testcase references.
|
||||
"""load testsuite with testcase references.
|
||||
support two different formats.
|
||||
|
||||
Args:
|
||||
@@ -315,7 +312,7 @@ def load_testsuite(raw_testsuite):
|
||||
|
||||
|
||||
def load_test_file(path):
|
||||
""" load test file, file maybe testcase/testsuite/api
|
||||
"""load test file, file maybe testcase/testsuite/api
|
||||
|
||||
Args:
|
||||
path (str): test file path
|
||||
@@ -390,7 +387,7 @@ def load_test_file(path):
|
||||
|
||||
|
||||
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
|
||||
|
||||
Args:
|
||||
@@ -402,7 +399,9 @@ def load_project_data(test_path, dot_env_path=None):
|
||||
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 = {}
|
||||
|
||||
@@ -428,7 +427,7 @@ def load_project_data(test_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:
|
||||
path (str): testcase/testsuite file/foler path.
|
||||
@@ -481,9 +480,7 @@ def load_cases(path, dot_env_path=None):
|
||||
|
||||
"""
|
||||
|
||||
tests_mapping = {
|
||||
"project_mapping": load_project_data(path, dot_env_path)
|
||||
}
|
||||
tests_mapping = {"project_mapping": load_project_data(path, dot_env_path)}
|
||||
|
||||
def __load_file_content(path):
|
||||
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_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)
|
||||
|
||||
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":
|
||||
absolute_base_path = 'file:///' + os.path.abspath(schemas_root_dir).replace("\\", "/") + '/'
|
||||
absolute_base_path = (
|
||||
"file:///" + os.path.abspath(schemas_root_dir).replace("\\", "/") + "/"
|
||||
)
|
||||
else:
|
||||
# Linux, Darwin
|
||||
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)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class JsonSchemaChecker(object):
|
||||
|
||||
@staticmethod
|
||||
def validate_format(content, scheme):
|
||||
""" check api/testcase/testsuite format if valid
|
||||
"""
|
||||
"""check api/testcase/testsuite format if valid"""
|
||||
try:
|
||||
jsonschema.validate(content, scheme, resolver=resolver)
|
||||
except jsonschema.exceptions.ValidationError as ex:
|
||||
@@ -57,37 +57,32 @@ class JsonSchemaChecker(object):
|
||||
|
||||
@staticmethod
|
||||
def validate_api_format(content):
|
||||
""" check api format if valid
|
||||
"""
|
||||
"""check api format if valid"""
|
||||
return JsonSchemaChecker.validate_format(content, api_schema)
|
||||
|
||||
@staticmethod
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
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)
|
||||
|
||||
|
||||
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:
|
||||
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):
|
||||
# path is a file
|
||||
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
|
||||
return False
|
||||
else:
|
||||
@@ -129,7 +124,7 @@ def is_test_path(path):
|
||||
|
||||
|
||||
def is_test_content(data_structure):
|
||||
""" check if data_structure is apis/testcases/testsuites.
|
||||
"""check if data_structure is apis/testcases/testsuites.
|
||||
|
||||
Args:
|
||||
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:
|
||||
# PyYAML version >= 5.1
|
||||
# ref: https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation
|
||||
yaml.warnings({'YAMLLoadWarning': False})
|
||||
yaml.warnings({"YAMLLoadWarning": False})
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def _load_yaml_file(yaml_file):
|
||||
""" load yaml file and check file content format
|
||||
"""
|
||||
with io.open(yaml_file, 'r', encoding='utf-8') as stream:
|
||||
"""load yaml file and check file content format"""
|
||||
with io.open(yaml_file, "r", encoding="utf-8") as stream:
|
||||
try:
|
||||
yaml_content = yaml.load(stream)
|
||||
except yaml.YAMLError as ex:
|
||||
@@ -32,13 +31,12 @@ def _load_yaml_file(yaml_file):
|
||||
|
||||
|
||||
def _load_json_file(json_file):
|
||||
""" load json file and check file content format
|
||||
"""
|
||||
with io.open(json_file, encoding='utf-8') as data_file:
|
||||
"""load json file and check file content format"""
|
||||
with io.open(json_file, encoding="utf-8") as data_file:
|
||||
try:
|
||||
json_content = json.load(data_file)
|
||||
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)
|
||||
raise exceptions.FileFormatError(err_msg)
|
||||
|
||||
@@ -46,7 +44,7 @@ def _load_json_file(json_file):
|
||||
|
||||
|
||||
def load_csv_file(csv_file):
|
||||
""" load csv file and check file content format
|
||||
"""load csv file and check file content format
|
||||
|
||||
Args:
|
||||
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 = []
|
||||
|
||||
with io.open(csv_file, encoding='utf-8') as csvfile:
|
||||
with io.open(csv_file, encoding="utf-8") as csvfile:
|
||||
reader = csv.DictReader(csvfile)
|
||||
for row in reader:
|
||||
csv_content_list.append(row)
|
||||
@@ -93,21 +91,21 @@ def load_file(file_path):
|
||||
raise exceptions.FileNotFound("{} does not exist.".format(file_path))
|
||||
|
||||
file_suffix = os.path.splitext(file_path)[1].lower()
|
||||
if file_suffix == '.json':
|
||||
if file_suffix == ".json":
|
||||
return _load_json_file(file_path)
|
||||
elif file_suffix in ['.yaml', '.yml']:
|
||||
elif file_suffix in [".yaml", ".yml"]:
|
||||
return _load_yaml_file(file_path)
|
||||
elif file_suffix == ".csv":
|
||||
return load_csv_file(file_path)
|
||||
else:
|
||||
# '' 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)
|
||||
return []
|
||||
|
||||
|
||||
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:
|
||||
folder_path (str): specified folder path to load
|
||||
@@ -132,7 +130,7 @@ def load_folder_files(folder_path, recursive=True):
|
||||
filenames_list = []
|
||||
|
||||
for filename in filenames:
|
||||
if not filename.endswith(('.yml', '.yaml', '.json')):
|
||||
if not filename.endswith((".yml", ".yaml", ".json")):
|
||||
continue
|
||||
|
||||
filenames_list.append(filename)
|
||||
@@ -148,7 +146,7 @@ def load_folder_files(folder_path, recursive=True):
|
||||
|
||||
|
||||
def load_dot_env_file(dot_env_path):
|
||||
""" load .env file.
|
||||
"""load .env file.
|
||||
|
||||
Args:
|
||||
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))
|
||||
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:
|
||||
# maxsplit=1
|
||||
if "=" in line:
|
||||
@@ -189,7 +187,7 @@ def load_dot_env_file(dot_env_path):
|
||||
|
||||
|
||||
def load_module_functions(module):
|
||||
""" load python module functions.
|
||||
"""load python module functions.
|
||||
|
||||
Args:
|
||||
module: python module
|
||||
@@ -213,7 +211,5 @@ def load_module_functions(module):
|
||||
|
||||
|
||||
def load_builtin_functions():
|
||||
""" load builtin module functions
|
||||
"""
|
||||
"""load builtin module functions"""
|
||||
return load_module_functions(builtin)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ project_working_directory = None
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
@@ -34,21 +34,25 @@ def locate_file(start_path, file_name):
|
||||
|
||||
# current working directory
|
||||
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
|
||||
# Windows, e.g. 'E:\\'
|
||||
# Linux/Darwin, '/'
|
||||
parent_dir = os.path.dirname(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
|
||||
return locate_file(parent_dir, file_name)
|
||||
|
||||
|
||||
def locate_debugtalk_py(start_path):
|
||||
""" locate debugtalk.py file
|
||||
"""locate debugtalk.py file
|
||||
|
||||
Args:
|
||||
start_path (str): start locating path,
|
||||
@@ -68,7 +72,7 @@ def locate_debugtalk_py(start_path):
|
||||
|
||||
|
||||
def init_project_working_directory(test_path):
|
||||
""" this should be called at startup
|
||||
"""this should be called at startup
|
||||
|
||||
run test file:
|
||||
run_path -> load_cases -> load_project_data -> init_project_working_directory
|
||||
|
||||
@@ -11,11 +11,11 @@ LOG_LEVEL = "INFO"
|
||||
LOG_FILE_PATH = ""
|
||||
|
||||
log_colors_config = {
|
||||
'DEBUG': 'cyan',
|
||||
'INFO': 'green',
|
||||
'WARNING': 'yellow',
|
||||
'ERROR': 'red',
|
||||
'CRITICAL': 'red',
|
||||
"DEBUG": "cyan",
|
||||
"INFO": "green",
|
||||
"WARNING": "yellow",
|
||||
"ERROR": "red",
|
||||
"CRITICAL": "red",
|
||||
}
|
||||
loggers = {}
|
||||
|
||||
@@ -58,10 +58,10 @@ def get_logger(name=None):
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
|
||||
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,
|
||||
reset=True,
|
||||
log_colors=log_colors_config
|
||||
log_colors=log_colors_config,
|
||||
)
|
||||
handler.setFormatter(formatter)
|
||||
_logger.addHandler(handler)
|
||||
@@ -81,8 +81,8 @@ def color_print(msg, color="WHITE"):
|
||||
|
||||
|
||||
def log_with_color(level):
|
||||
""" log with color by different level
|
||||
"""
|
||||
"""log with color by different level"""
|
||||
|
||||
def wrapper(text):
|
||||
color = log_colors_config[level.upper()]
|
||||
_logger = get_logger()
|
||||
|
||||
@@ -27,7 +27,7 @@ def get_parse_failed_testfiles():
|
||||
|
||||
|
||||
def parse_string_value(str_value):
|
||||
""" parse string to number if possible
|
||||
"""parse string to number if possible
|
||||
e.g. "123" => 123
|
||||
"12.2" => 12.3
|
||||
"abc" => "abc"
|
||||
@@ -43,8 +43,7 @@ def parse_string_value(str_value):
|
||||
|
||||
|
||||
def is_var_or_func_exist(content):
|
||||
""" check if variable or function exist
|
||||
"""
|
||||
"""check if variable or function exist"""
|
||||
if not isinstance(content, basestring):
|
||||
return False
|
||||
|
||||
@@ -71,7 +70,7 @@ def is_var_or_func_exist(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:
|
||||
content (str): string content
|
||||
@@ -96,16 +95,14 @@ def regex_findall_variables(content):
|
||||
try:
|
||||
vars_list = []
|
||||
for var_tuple in variable_regex_compile.findall(content):
|
||||
vars_list.append(
|
||||
var_tuple[0] or var_tuple[1]
|
||||
)
|
||||
vars_list.append(var_tuple[0] or var_tuple[1])
|
||||
return vars_list
|
||||
except TypeError:
|
||||
return []
|
||||
|
||||
|
||||
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:
|
||||
content (str): string content
|
||||
@@ -137,7 +134,7 @@ def regex_findall_functions(content):
|
||||
|
||||
|
||||
def parse_parameters(parameters, variables_mapping=None, functions_mapping=None):
|
||||
""" parse parameters and generate cartesian product.
|
||||
"""parse parameters and generate cartesian product.
|
||||
|
||||
Args:
|
||||
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)
|
||||
else:
|
||||
# (2) & (3)
|
||||
parsed_variables_mapping = parse_variables_mapping(
|
||||
variables_mapping
|
||||
)
|
||||
parsed_variables_mapping = parse_variables_mapping(variables_mapping)
|
||||
parsed_parameter_content = eval_lazy_data(
|
||||
parameter_content,
|
||||
parsed_variables_mapping,
|
||||
functions_mapping
|
||||
parameter_content, parsed_variables_mapping, functions_mapping
|
||||
)
|
||||
if not isinstance(parsed_parameter_content, list):
|
||||
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": "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)):
|
||||
# {"username-password": "${get_account()}"}
|
||||
# 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:
|
||||
# {"user_agent": "${get_user_agent()}"}
|
||||
# get_user_agent() => ["iOS/10.1", "iOS/10.2"]
|
||||
parameter_dict = {
|
||||
parameter_name_list[0]: parameter_item
|
||||
}
|
||||
parameter_dict = {parameter_name_list[0]: parameter_item}
|
||||
|
||||
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):
|
||||
""" convert comparator alias to uniform name
|
||||
"""
|
||||
"""convert comparator alias to uniform name"""
|
||||
if comparator in ["eq", "equals", "==", "is"]:
|
||||
return "equals"
|
||||
elif comparator in ["lt", "less_than"]:
|
||||
@@ -248,22 +240,35 @@ def get_uniform_comparator(comparator):
|
||||
return "string_equals"
|
||||
elif comparator in ["len_eq", "length_equals", "count_eq"]:
|
||||
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"
|
||||
elif comparator in ["len_ge", "count_ge", "length_greater_than_or_equals",
|
||||
"count_greater_than_or_equals"]:
|
||||
elif comparator in [
|
||||
"len_ge",
|
||||
"count_ge",
|
||||
"length_greater_than_or_equals",
|
||||
"count_greater_than_or_equals",
|
||||
]:
|
||||
return "length_greater_than_or_equals"
|
||||
elif comparator in ["len_lt", "count_lt", "length_less_than", "count_less_than"]:
|
||||
return "length_less_than"
|
||||
elif comparator in ["len_le", "count_le", "length_less_than_or_equals",
|
||||
"count_less_than_or_equals"]:
|
||||
elif comparator in [
|
||||
"len_le",
|
||||
"count_le",
|
||||
"length_less_than_or_equals",
|
||||
"count_less_than_or_equals",
|
||||
]:
|
||||
return "length_less_than_or_equals"
|
||||
else:
|
||||
return comparator
|
||||
|
||||
|
||||
def uniform_validator(validator):
|
||||
""" unify validator
|
||||
"""unify validator
|
||||
|
||||
Args:
|
||||
validator (dict): validator maybe in two formats:
|
||||
@@ -310,15 +315,11 @@ def uniform_validator(validator):
|
||||
# uniform comparator, e.g. lt => less_than, eq => equals
|
||||
comparator = get_uniform_comparator(comparator)
|
||||
|
||||
return {
|
||||
"check": check_item,
|
||||
"expect": expect_value,
|
||||
"comparator": comparator
|
||||
}
|
||||
return {"check": check_item, "expect": expect_value, "comparator": comparator}
|
||||
|
||||
|
||||
def _convert_validators_to_mapping(validators):
|
||||
""" convert validators list to mapping.
|
||||
"""convert validators list to mapping.
|
||||
|
||||
Args:
|
||||
validators (list): validators in list
|
||||
@@ -353,7 +354,7 @@ def _convert_validators_to_mapping(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.
|
||||
|
||||
Args:
|
||||
@@ -393,8 +394,9 @@ def extend_validators(raw_validators, override_validators):
|
||||
## parse content with variables and functions mapping
|
||||
###############################################################################
|
||||
|
||||
|
||||
def get_mapping_variable(variable_name, variables_mapping):
|
||||
""" get variable from variables_mapping.
|
||||
"""get variable from variables_mapping.
|
||||
|
||||
Args:
|
||||
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):
|
||||
""" get function from functions_mapping,
|
||||
"""get function from functions_mapping,
|
||||
if not found, then try to check if builtin function.
|
||||
|
||||
Args:
|
||||
@@ -440,6 +442,7 @@ def get_mapping_function(function_name, functions_mapping):
|
||||
elif function_name in ["multipart_encoder", "multipart_content_type"]:
|
||||
# extension for upload test
|
||||
from httprunner.ext import uploader
|
||||
|
||||
return getattr(uploader, function_name)
|
||||
|
||||
try:
|
||||
@@ -459,7 +462,7 @@ def get_mapping_function(function_name, functions_mapping):
|
||||
|
||||
|
||||
def parse_function_params(params):
|
||||
""" parse function params to args and kwargs.
|
||||
"""parse function params to args and kwargs.
|
||||
|
||||
Args:
|
||||
params (str): function param in string
|
||||
@@ -489,20 +492,17 @@ def parse_function_params(params):
|
||||
{'args': [1, 2], 'kwargs': {'a':3, 'b':4}}
|
||||
|
||||
"""
|
||||
function_meta = {
|
||||
"args": [],
|
||||
"kwargs": {}
|
||||
}
|
||||
function_meta = {"args": [], "kwargs": {}}
|
||||
|
||||
params_str = params.strip()
|
||||
if params_str == "":
|
||||
return function_meta
|
||||
|
||||
args_list = params_str.split(',')
|
||||
args_list = params_str.split(",")
|
||||
for arg in args_list:
|
||||
arg = arg.strip()
|
||||
if '=' in arg:
|
||||
key, value = arg.split('=')
|
||||
if "=" in arg:
|
||||
key, value = arg.split("=")
|
||||
function_meta["kwargs"][key.strip()] = parse_string_value(value.strip())
|
||||
else:
|
||||
function_meta["args"].append(parse_string_value(arg))
|
||||
@@ -511,11 +511,10 @@ def parse_function_params(params):
|
||||
|
||||
|
||||
class LazyFunction(object):
|
||||
""" call function lazily.
|
||||
"""
|
||||
"""call function lazily."""
|
||||
|
||||
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:
|
||||
function_meta (dict): function name, args and kwargs.
|
||||
@@ -532,25 +531,24 @@ class LazyFunction(object):
|
||||
self.__parse(function_meta)
|
||||
|
||||
def __parse(self, function_meta):
|
||||
""" init func as lazy functon instance
|
||||
"""init func as lazy functon instance
|
||||
|
||||
Args:
|
||||
function_meta (dict): function meta including name, args and kwargs
|
||||
"""
|
||||
self._func = get_mapping_function(
|
||||
function_meta["func_name"],
|
||||
self.functions_mapping
|
||||
function_meta["func_name"], self.functions_mapping
|
||||
)
|
||||
self.func_name = self._func.__name__
|
||||
self._args = prepare_lazy_data(
|
||||
function_meta.get("args", []),
|
||||
self.functions_mapping,
|
||||
self.check_variables_set
|
||||
self.check_variables_set,
|
||||
)
|
||||
self._kwargs = prepare_lazy_data(
|
||||
function_meta.get("kwargs", {}),
|
||||
self.functions_mapping,
|
||||
self.check_variables_set
|
||||
self.check_variables_set,
|
||||
)
|
||||
|
||||
if self.func_name == "load_csv_file":
|
||||
@@ -578,8 +576,7 @@ class LazyFunction(object):
|
||||
if self._kwargs:
|
||||
args_string += ", "
|
||||
str_kwargs = [
|
||||
"{}={}".format(key, str(value))
|
||||
for key, value in self._kwargs.items()
|
||||
"{}={}".format(key, str(value)) for key, value in self._kwargs.items()
|
||||
]
|
||||
args_string += ", ".join(str_kwargs)
|
||||
|
||||
@@ -589,8 +586,8 @@ class LazyFunction(object):
|
||||
return self.func_name, repr(args), repr(kwargs)
|
||||
|
||||
def to_value(self, variables_mapping=None):
|
||||
""" parse lazy data with evaluated variables mapping.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""parse lazy data with evaluated variables mapping.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""
|
||||
variables_mapping = variables_mapping or {}
|
||||
args = parse_lazy_data(self._args, variables_mapping)
|
||||
@@ -605,12 +602,13 @@ cached_functions_mapping = {}
|
||||
|
||||
|
||||
class LazyString(object):
|
||||
""" evaluate string lazily.
|
||||
"""
|
||||
"""evaluate string lazily."""
|
||||
|
||||
def __init__(self, raw_string, functions_mapping=None, check_variables_set=None, cached=False):
|
||||
""" make raw_string as lazy object with functions_mapping
|
||||
check if any variable undefined in check_variables_set
|
||||
def __init__(
|
||||
self, raw_string, functions_mapping=None, check_variables_set=None, cached=False
|
||||
):
|
||||
"""make raw_string as lazy object with functions_mapping
|
||||
check if any variable undefined in check_variables_set
|
||||
"""
|
||||
self.raw_string = raw_string
|
||||
self.functions_mapping = functions_mapping or {}
|
||||
@@ -619,7 +617,7 @@ class LazyString(object):
|
||||
self.__parse(raw_string)
|
||||
|
||||
def __parse(self, raw_string):
|
||||
""" parse raw string, replace function and variable with {}
|
||||
"""parse raw string, replace function and variable with {}
|
||||
|
||||
Args:
|
||||
raw_string(str): string with functions or varialbes
|
||||
@@ -658,14 +656,10 @@ class LazyString(object):
|
||||
# search function like ${func($a, $b)}
|
||||
func_match = function_regex_compile.match(raw_string, match_start_position)
|
||||
if func_match:
|
||||
function_meta = {
|
||||
"func_name": func_match.group(1)
|
||||
}
|
||||
function_meta = {"func_name": func_match.group(1)}
|
||||
function_meta.update(parse_function_params(func_match.group(2)))
|
||||
lazy_func = LazyFunction(
|
||||
function_meta,
|
||||
self.functions_mapping,
|
||||
self.check_variables_set
|
||||
function_meta, self.functions_mapping, self.check_variables_set
|
||||
)
|
||||
self._args.append(lazy_func)
|
||||
match_start_position = func_match.end()
|
||||
@@ -701,15 +695,19 @@ class LazyString(object):
|
||||
return "LazyString({})".format(self.raw_string)
|
||||
|
||||
def to_value(self, variables_mapping=None):
|
||||
""" parse lazy data with evaluated variables mapping.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""parse lazy data with evaluated variables mapping.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""
|
||||
variables_mapping = variables_mapping or {}
|
||||
|
||||
args = []
|
||||
for arg in self._args:
|
||||
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]
|
||||
else:
|
||||
value = arg.to_value(variables_mapping)
|
||||
@@ -726,8 +724,10 @@ class LazyString(object):
|
||||
return self._string.format(*args)
|
||||
|
||||
|
||||
def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None, cached=False):
|
||||
""" make string in content as lazy object with functions_mapping
|
||||
def prepare_lazy_data(
|
||||
content, functions_mapping=None, check_variables_set=None, cached=False
|
||||
):
|
||||
"""make string in content as lazy object with functions_mapping
|
||||
|
||||
Raises:
|
||||
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)):
|
||||
return [
|
||||
prepare_lazy_data(
|
||||
item,
|
||||
functions_mapping,
|
||||
check_variables_set,
|
||||
cached
|
||||
)
|
||||
prepare_lazy_data(item, functions_mapping, check_variables_set, cached)
|
||||
for item in content
|
||||
]
|
||||
|
||||
@@ -752,16 +747,10 @@ def prepare_lazy_data(content, functions_mapping=None, check_variables_set=None,
|
||||
parsed_content = {}
|
||||
for key, value in content.items():
|
||||
parsed_key = prepare_lazy_data(
|
||||
key,
|
||||
functions_mapping,
|
||||
check_variables_set,
|
||||
cached
|
||||
key, functions_mapping, check_variables_set, cached
|
||||
)
|
||||
parsed_value = prepare_lazy_data(
|
||||
value,
|
||||
functions_mapping,
|
||||
check_variables_set,
|
||||
cached
|
||||
value, functions_mapping, check_variables_set, cached
|
||||
)
|
||||
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):
|
||||
""" parse lazy data with evaluated variables mapping.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""parse lazy data with evaluated variables mapping.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""
|
||||
# TODO: refactor type check
|
||||
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)
|
||||
|
||||
elif isinstance(content, (list, set, tuple)):
|
||||
return [
|
||||
parse_lazy_data(item, variables_mapping)
|
||||
for item in content
|
||||
]
|
||||
return [parse_lazy_data(item, variables_mapping) for item in content]
|
||||
|
||||
elif isinstance(content, dict):
|
||||
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):
|
||||
""" evaluate data instantly.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""evaluate data instantly.
|
||||
Notice: variables_mapping should not contain any variable or function.
|
||||
"""
|
||||
variables_mapping = variables_mapping or {}
|
||||
check_variables_set = set(variables_mapping.keys())
|
||||
return parse_lazy_data(
|
||||
prepare_lazy_data(
|
||||
content,
|
||||
functions_mapping,
|
||||
check_variables_set
|
||||
),
|
||||
variables_mapping
|
||||
prepare_lazy_data(content, functions_mapping, check_variables_set),
|
||||
variables_mapping,
|
||||
)
|
||||
|
||||
|
||||
def extract_variables(content):
|
||||
""" extract all variables in content recursively.
|
||||
"""
|
||||
"""extract all variables in content recursively."""
|
||||
if isinstance(content, (list, set, tuple)):
|
||||
variables = set()
|
||||
for item in content:
|
||||
@@ -850,7 +831,7 @@ def extract_variables(content):
|
||||
|
||||
|
||||
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:
|
||||
variables_mapping (dict):
|
||||
@@ -911,7 +892,12 @@ def parse_variables_mapping(variables_mapping):
|
||||
# reference other variable, or function call with other variable
|
||||
# e.g. {"varA": "123$varB", "varB": "456$varC"}
|
||||
# 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
|
||||
continue
|
||||
|
||||
@@ -922,7 +908,7 @@ def parse_variables_mapping(variables_mapping):
|
||||
|
||||
|
||||
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:
|
||||
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
|
||||
def_variables = api_def_dict.pop("variables", [])
|
||||
test_dict["variables"] = utils.extend_variables(
|
||||
def_variables,
|
||||
test_dict.get("variables", {})
|
||||
def_variables, test_dict.get("variables", {})
|
||||
)
|
||||
|
||||
# merge & override validators TODO: relocate
|
||||
def_raw_validators = api_def_dict.pop("validate", [])
|
||||
def_validators = [
|
||||
uniform_validator(_validator)
|
||||
for _validator in def_raw_validators
|
||||
uniform_validator(_validator) for _validator in def_raw_validators
|
||||
]
|
||||
ref_validators = test_dict.pop("validate", [])
|
||||
test_dict["validate"] = extend_validators(
|
||||
def_validators,
|
||||
ref_validators
|
||||
)
|
||||
test_dict["validate"] = extend_validators(def_validators, ref_validators)
|
||||
|
||||
# merge & override extractors
|
||||
def_extrators = api_def_dict.pop("extract", {})
|
||||
test_dict["extract"] = utils.extend_variables(
|
||||
def_extrators,
|
||||
test_dict.get("extract", {})
|
||||
def_extrators, test_dict.get("extract", {})
|
||||
)
|
||||
|
||||
# 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):
|
||||
""" extend test with testcase definition
|
||||
"""extend test with testcase definition
|
||||
test will merge and override testcase config definition.
|
||||
|
||||
Args:
|
||||
@@ -1024,7 +1004,8 @@ def _extend_with_testcase(test_dict, testcase_def_dict):
|
||||
# override testcase config variables
|
||||
testcase_def_dict["config"].setdefault("variables", {})
|
||||
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_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
|
||||
|
||||
# override name
|
||||
test_name = test_dict.pop("name", None) \
|
||||
or testcase_def_dict["config"].pop("name", None) \
|
||||
or "testcase name undefined"
|
||||
test_name = (
|
||||
test_dict.pop("name", None)
|
||||
or testcase_def_dict["config"].pop("name", None)
|
||||
or "testcase name undefined"
|
||||
)
|
||||
|
||||
# override testcase config name, output, etc.
|
||||
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):
|
||||
""" parse testcase/testsuite config.
|
||||
"""
|
||||
"""parse testcase/testsuite config."""
|
||||
# get config 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", {})
|
||||
|
||||
if isinstance(raw_config_variables, basestring) and function_regex_compile.match(
|
||||
raw_config_variables):
|
||||
raw_config_variables
|
||||
):
|
||||
# config variables are generated by calling function
|
||||
# e.g.
|
||||
# "config": {
|
||||
@@ -1077,13 +1060,17 @@ def __prepare_config(config, project_mapping, session_variables_set=None):
|
||||
config["variables"] = raw_config_variables_mapping
|
||||
|
||||
check_variables_set = set(raw_config_variables_mapping.keys())
|
||||
check_variables_set |= (session_variables_set or set())
|
||||
prepared_config = prepare_lazy_data(config, functions, check_variables_set, cached=True)
|
||||
check_variables_set |= session_variables_set or set()
|
||||
prepared_config = prepare_lazy_data(
|
||||
config, functions, check_variables_set, cached=True
|
||||
)
|
||||
return prepared_config
|
||||
|
||||
|
||||
def __prepare_testcase_tests(tests, config, project_mapping, session_variables_set=None):
|
||||
""" override tests with testcase config variables, base_url and verify.
|
||||
def __prepare_testcase_tests(
|
||||
tests, config, project_mapping, session_variables_set=None
|
||||
):
|
||||
"""override tests with testcase config variables, base_url and verify.
|
||||
test maybe nested testcase.
|
||||
|
||||
variables priority:
|
||||
@@ -1107,7 +1094,9 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
||||
functions = project_mapping.get("functions", {})
|
||||
|
||||
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:
|
||||
|
||||
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
|
||||
# override test_dict variables
|
||||
test_dict_variables = utils.extend_variables(
|
||||
test_dict.pop("variables", {}),
|
||||
config_variables
|
||||
test_dict.pop("variables", {}), config_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:
|
||||
ref_raw_validators = test_dict.pop("validate", [])
|
||||
test_dict["validate"] = [
|
||||
uniform_validator(_validator)
|
||||
for _validator in ref_raw_validators
|
||||
uniform_validator(_validator) for _validator in ref_raw_validators
|
||||
]
|
||||
|
||||
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)
|
||||
|
||||
# 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:
|
||||
continue
|
||||
|
||||
@@ -1169,6 +1158,7 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
||||
|
||||
if "upload" in test_dict["request"]:
|
||||
from httprunner.ext.uploader import prepare_upload_test
|
||||
|
||||
prepare_upload_test(test_dict)
|
||||
|
||||
# current teststep variables
|
||||
@@ -1187,27 +1177,18 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
||||
for _validator in validators:
|
||||
function_meta = {
|
||||
"func_name": _validator["comparator"],
|
||||
"args": [
|
||||
_validator["check"],
|
||||
_validator["expect"]
|
||||
],
|
||||
"kwargs": {}
|
||||
"args": [_validator["check"], _validator["expect"]],
|
||||
"kwargs": {},
|
||||
}
|
||||
prepared_validators.append(
|
||||
LazyFunction(
|
||||
function_meta,
|
||||
functions,
|
||||
teststep_variables_set
|
||||
)
|
||||
LazyFunction(function_meta, functions, teststep_variables_set)
|
||||
)
|
||||
test_dict["validate"] = prepared_validators
|
||||
|
||||
# convert variables and functions to lazy object.
|
||||
# raises VariableNotFound if undefined variable exists in test_dict
|
||||
prepared_test_dict = prepare_lazy_data(
|
||||
test_dict,
|
||||
functions,
|
||||
teststep_variables_set
|
||||
test_dict, functions, teststep_variables_set
|
||||
)
|
||||
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):
|
||||
""" parse testcase
|
||||
"""parse testcase
|
||||
|
||||
Args:
|
||||
testcase (dict):
|
||||
@@ -1229,25 +1210,22 @@ def _parse_testcase(testcase, project_mapping, session_variables_set=None):
|
||||
|
||||
try:
|
||||
prepared_config = __prepare_config(
|
||||
testcase["config"],
|
||||
project_mapping,
|
||||
session_variables_set
|
||||
testcase["config"], project_mapping, session_variables_set
|
||||
)
|
||||
prepared_testcase_tests = __prepare_testcase_tests(
|
||||
testcase["teststeps"],
|
||||
prepared_config,
|
||||
project_mapping,
|
||||
session_variables_set
|
||||
session_variables_set,
|
||||
)
|
||||
return {
|
||||
"config": prepared_config,
|
||||
"teststeps": prepared_testcase_tests
|
||||
}
|
||||
return {"config": prepared_config, "teststeps": prepared_testcase_tests}
|
||||
except (exceptions.MyBaseFailure, exceptions.MyBaseError) as ex:
|
||||
testcase_type = testcase["type"]
|
||||
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
|
||||
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):
|
||||
""" override testscases with testsuite config variables, base_url and verify.
|
||||
"""override testscases with testsuite config variables, base_url and verify.
|
||||
|
||||
variables priority:
|
||||
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
|
||||
# override test_dict variables
|
||||
testcase_config_variables = utils.extend_variables(
|
||||
testcase.pop("variables", {}),
|
||||
testsuite_config_variables
|
||||
testcase.pop("variables", {}), testsuite_config_variables
|
||||
)
|
||||
|
||||
# 2, testcase config > testcase_def config
|
||||
# override testcase_def config variables
|
||||
overrided_testcase_config_variables = utils.extend_variables(
|
||||
parsed_testcase["config"].pop("variables", {}),
|
||||
testcase_config_variables
|
||||
parsed_testcase["config"].pop("variables", {}), testcase_config_variables
|
||||
)
|
||||
|
||||
if overrided_testcase_config_variables:
|
||||
parsed_testcase["config"]["variables"] = overrided_testcase_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
|
||||
if "parameters" in testcase and testcase["parameters"]:
|
||||
cartesian_product_parameters = parse_parameters(
|
||||
testcase["parameters"],
|
||||
parsed_config_variables,
|
||||
functions
|
||||
testcase["parameters"], parsed_config_variables, functions
|
||||
)
|
||||
|
||||
for parameter_variables in cartesian_product_parameters:
|
||||
# deepcopy to avoid influence between parameters
|
||||
testcase_copied = utils.deepcopy_dict(parsed_testcase)
|
||||
parsed_config_variables_copied = utils.deepcopy_dict(parsed_config_variables)
|
||||
testcase_copied["config"]["variables"] = utils.extend_variables(
|
||||
parsed_config_variables_copied,
|
||||
parameter_variables
|
||||
parsed_config_variables_copied = utils.deepcopy_dict(
|
||||
parsed_config_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:
|
||||
continue
|
||||
parsed_testcase_copied["config"]["name"] = parse_lazy_data(
|
||||
parsed_testcase_copied["config"]["name"],
|
||||
testcase_copied["config"]["variables"]
|
||||
testcase_copied["config"]["variables"],
|
||||
)
|
||||
parsed_testcase_list.append(parsed_testcase_copied)
|
||||
|
||||
@@ -1378,15 +1357,13 @@ def _parse_testsuite(testsuite, project_mapping):
|
||||
testsuite.setdefault("config", {})
|
||||
prepared_config = __prepare_config(testsuite["config"], project_mapping)
|
||||
parsed_testcase_list = __get_parsed_testsuite_testcases(
|
||||
testsuite["testcases"],
|
||||
prepared_config,
|
||||
project_mapping
|
||||
testsuite["testcases"], prepared_config, project_mapping
|
||||
)
|
||||
return parsed_testcase_list
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
@@ -1475,12 +1452,10 @@ def parse_tests(tests_mapping):
|
||||
# encapsulate api as a testcase
|
||||
for api_content in tests_mapping["apis"]:
|
||||
testcase = {
|
||||
"config": {
|
||||
"name": api_content.get("name")
|
||||
},
|
||||
"config": {"name": api_content.get("name")},
|
||||
"teststeps": [api_content],
|
||||
"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)
|
||||
if not parsed_testcase:
|
||||
|
||||
@@ -16,5 +16,5 @@ __all__ = [
|
||||
"get_summary",
|
||||
"stringify_summary",
|
||||
"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.gen_report import gen_html_report
|
||||
|
||||
__all__ = [
|
||||
"HtmlTestResult",
|
||||
"gen_html_report"
|
||||
]
|
||||
__all__ = ["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):
|
||||
""" render html report with specified report name and template
|
||||
"""render html report with specified report name and template
|
||||
|
||||
Args:
|
||||
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:
|
||||
report_template = os.path.join(
|
||||
os.path.abspath(os.path.dirname(__file__)),
|
||||
"template.html"
|
||||
os.path.abspath(os.path.dirname(__file__)), "template.html"
|
||||
)
|
||||
logger.log_debug("No html report template specified, use default.")
|
||||
else:
|
||||
@@ -43,22 +42,22 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
|
||||
else:
|
||||
report_dir = report_dir or os.path.join(os.getcwd(), "reports")
|
||||
# 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):
|
||||
os.makedirs(report_dir)
|
||||
|
||||
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()
|
||||
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(
|
||||
template_content,
|
||||
extensions=["jinja2.ext.loopcontrols"]
|
||||
template_content, extensions=["jinja2.ext.loopcontrols"]
|
||||
).render(summary)
|
||||
fp_w.write(rendered_content)
|
||||
|
||||
logger.log_info("Generated Html report: {}".format(report_path))
|
||||
|
||||
return report_path
|
||||
|
||||
|
||||
@@ -5,19 +5,20 @@ from httprunner import logger
|
||||
|
||||
|
||||
class HtmlTestResult(unittest.TextTestResult):
|
||||
""" A html result class that can generate formatted html results.
|
||||
Used by TextTestRunner.
|
||||
"""A html result class that can generate formatted html results.
|
||||
Used by TextTestRunner.
|
||||
"""
|
||||
|
||||
def __init__(self, stream, descriptions, verbosity):
|
||||
super(HtmlTestResult, self).__init__(stream, descriptions, verbosity)
|
||||
self.records = []
|
||||
|
||||
def _record_test(self, test, status, attachment=''):
|
||||
def _record_test(self, test, status, attachment=""):
|
||||
data = {
|
||||
'name': test.shortDescription(),
|
||||
'status': status,
|
||||
'attachment': attachment,
|
||||
"meta_datas": test.meta_datas
|
||||
"name": test.shortDescription(),
|
||||
"status": status,
|
||||
"attachment": attachment,
|
||||
"meta_datas": test.meta_datas,
|
||||
}
|
||||
self.records.append(data)
|
||||
|
||||
@@ -25,38 +26,38 @@ class HtmlTestResult(unittest.TextTestResult):
|
||||
self.start_at = time.time()
|
||||
|
||||
def startTest(self, test):
|
||||
""" add start test time """
|
||||
"""add start test time"""
|
||||
super(HtmlTestResult, self).startTest(test)
|
||||
logger.color_print(test.shortDescription(), "yellow")
|
||||
|
||||
def addSuccess(self, test):
|
||||
super(HtmlTestResult, self).addSuccess(test)
|
||||
self._record_test(test, 'success')
|
||||
self._record_test(test, "success")
|
||||
print("")
|
||||
|
||||
def addError(self, 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("")
|
||||
|
||||
def addFailure(self, 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("")
|
||||
|
||||
def addSkip(self, test, reason):
|
||||
super(HtmlTestResult, self).addSkip(test, reason)
|
||||
self._record_test(test, 'skipped', reason)
|
||||
self._record_test(test, "skipped", reason)
|
||||
print("")
|
||||
|
||||
def addExpectedFailure(self, 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("")
|
||||
|
||||
def addUnexpectedSuccess(self, test):
|
||||
super(HtmlTestResult, self).addUnexpectedSuccess(test)
|
||||
self._record_test(test, 'UnexpectedSuccess')
|
||||
self._record_test(test, "UnexpectedSuccess")
|
||||
print("")
|
||||
|
||||
@property
|
||||
|
||||
@@ -9,43 +9,37 @@ from httprunner import __version__
|
||||
|
||||
|
||||
def prepare_event_kwargs(event_name, params):
|
||||
""" prepare report event kwargs"""
|
||||
"""prepare report event kwargs"""
|
||||
|
||||
kwargs = {
|
||||
"headers": {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
"headers": {"content-type": "application/json"},
|
||||
"json": {
|
||||
"user": {
|
||||
"user_unique_id": str(uuid.getnode())
|
||||
},
|
||||
"user": {"user_unique_id": str(uuid.getnode())},
|
||||
"header": {
|
||||
"app_id": 173519,
|
||||
"os_name": platform.system(),
|
||||
"os_version": platform.release(),
|
||||
"app_version": __version__ # HttpRunner version
|
||||
"app_version": __version__, # HttpRunner version
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"event": event_name,
|
||||
"params": json.dumps(params),
|
||||
"time": int(time.time())
|
||||
"time": int(time.time()),
|
||||
}
|
||||
],
|
||||
"verbose": 1
|
||||
}
|
||||
"verbose": 1,
|
||||
},
|
||||
}
|
||||
return kwargs
|
||||
|
||||
|
||||
def report_event(event_name, success=True):
|
||||
params = {
|
||||
"success": 1 if success else 0
|
||||
}
|
||||
params = {"success": 1 if success else 0}
|
||||
kwargs = prepare_event_kwargs(event_name, params)
|
||||
resp = requests.post("http://mcs.snssdk.com/v1/json", **kwargs)
|
||||
print("resp---", resp.json())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
report_event("loader")
|
||||
|
||||
@@ -8,7 +8,7 @@ from httprunner.compat import basestring, bytes, json, numeric_types, JSONDecode
|
||||
|
||||
|
||||
def dumps_json(value):
|
||||
""" dumps json value to indented string
|
||||
"""dumps json value to indented string
|
||||
|
||||
Args:
|
||||
value (dict): raw json data
|
||||
@@ -28,7 +28,7 @@ def detect_encoding(value):
|
||||
|
||||
|
||||
def __stringify_request(request_data):
|
||||
""" stringfy HTTP request data
|
||||
"""stringfy HTTP request data
|
||||
|
||||
Args:
|
||||
request_data (dict): HTTP request data in dict.
|
||||
@@ -84,7 +84,7 @@ def __stringify_request(request_data):
|
||||
|
||||
|
||||
def __stringify_response(response_data):
|
||||
""" stringfy HTTP response data
|
||||
"""stringfy HTTP response data
|
||||
|
||||
Args:
|
||||
response_data (dict):
|
||||
@@ -124,8 +124,7 @@ def __stringify_response(response_data):
|
||||
if key == "body" and "image" in response_data["content_type"]:
|
||||
# display image
|
||||
value = "data:{};base64,{}".format(
|
||||
response_data["content_type"],
|
||||
b64encode(value).decode(encoding)
|
||||
response_data["content_type"], b64encode(value).decode(encoding)
|
||||
)
|
||||
else:
|
||||
value = escape(value.decode(encoding))
|
||||
@@ -143,7 +142,7 @@ def __stringify_response(response_data):
|
||||
|
||||
|
||||
def __expand_meta_datas(meta_datas, meta_datas_expanded):
|
||||
""" expand meta_datas to one level
|
||||
"""expand meta_datas to one level
|
||||
|
||||
Args:
|
||||
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):
|
||||
""" caculate total response time of all meta_datas
|
||||
"""
|
||||
"""caculate total response time of all meta_datas"""
|
||||
try:
|
||||
response_time = 0
|
||||
for meta_data in meta_datas_expanded:
|
||||
@@ -200,15 +198,14 @@ def __stringify_meta_datas(meta_datas):
|
||||
|
||||
|
||||
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"]):
|
||||
|
||||
if not suite_summary.get("name"):
|
||||
suite_summary["name"] = "testcase {}".format(index)
|
||||
|
||||
for record in suite_summary.get("records"):
|
||||
meta_datas = record['meta_datas']
|
||||
meta_datas = record["meta_datas"]
|
||||
__stringify_meta_datas(meta_datas)
|
||||
meta_datas_expanded = []
|
||||
__expand_meta_datas(meta_datas, meta_datas_expanded)
|
||||
|
||||
@@ -7,15 +7,14 @@ def get_platform():
|
||||
return {
|
||||
"httprunner_version": __version__,
|
||||
"python_version": "{} {}".format(
|
||||
platform.python_implementation(),
|
||||
platform.python_version()
|
||||
platform.python_implementation(), platform.python_version()
|
||||
),
|
||||
"platform": platform.platform()
|
||||
"platform": platform.platform(),
|
||||
}
|
||||
|
||||
|
||||
def aggregate_stat(origin_stat, new_stat):
|
||||
""" aggregate new_stat to origin_stat.
|
||||
"""aggregate new_stat to origin_stat.
|
||||
|
||||
Args:
|
||||
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"])
|
||||
elif key == "duration":
|
||||
# duration = max_end_time - min_start_time
|
||||
max_end_time = max(origin_stat["start_at"] + origin_stat["duration"],
|
||||
new_stat["start_at"] + new_stat["duration"])
|
||||
max_end_time = max(
|
||||
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"])
|
||||
origin_stat["duration"] = max_end_time - min_start_time
|
||||
else:
|
||||
@@ -39,7 +40,7 @@ def aggregate_stat(origin_stat, new_stat):
|
||||
|
||||
|
||||
def get_summary(result):
|
||||
""" get summary from test result
|
||||
"""get summary from test result
|
||||
|
||||
Args:
|
||||
result (instance): HtmlTestResult() instance
|
||||
@@ -58,25 +59,24 @@ def get_summary(result):
|
||||
summary = {
|
||||
"success": result.wasSuccessful(),
|
||||
"stat": {
|
||||
'total': result.testsRun,
|
||||
'failures': len(result.failures),
|
||||
'errors': len(result.errors),
|
||||
'skipped': len(result.skipped),
|
||||
'expectedFailures': len(result.expectedFailures),
|
||||
'unexpectedSuccesses': len(result.unexpectedSuccesses)
|
||||
}
|
||||
"total": result.testsRun,
|
||||
"failures": len(result.failures),
|
||||
"errors": len(result.errors),
|
||||
"skipped": len(result.skipped),
|
||||
"expectedFailures": len(result.expectedFailures),
|
||||
"unexpectedSuccesses": len(result.unexpectedSuccesses),
|
||||
},
|
||||
}
|
||||
summary["stat"]["successes"] = summary["stat"]["total"] \
|
||||
- summary["stat"]["failures"] \
|
||||
- summary["stat"]["errors"] \
|
||||
- summary["stat"]["skipped"] \
|
||||
- summary["stat"]["expectedFailures"] \
|
||||
- summary["stat"]["unexpectedSuccesses"]
|
||||
summary["stat"]["successes"] = (
|
||||
summary["stat"]["total"]
|
||||
- summary["stat"]["failures"]
|
||||
- summary["stat"]["errors"]
|
||||
- summary["stat"]["skipped"]
|
||||
- summary["stat"]["expectedFailures"]
|
||||
- summary["stat"]["unexpectedSuccesses"]
|
||||
)
|
||||
|
||||
summary["time"] = {
|
||||
'start_at': result.start_at,
|
||||
'duration': result.duration
|
||||
}
|
||||
summary["time"] = {"start_at": result.start_at, "duration": result.duration}
|
||||
summary["records"] = result.records
|
||||
|
||||
return summary
|
||||
|
||||
@@ -10,9 +10,8 @@ text_extractor_regexp_compile = re.compile(r".*\(.*\).*")
|
||||
|
||||
|
||||
class ResponseObject(object):
|
||||
|
||||
def __init__(self, resp_obj):
|
||||
""" initialize with a requests.Response object
|
||||
"""initialize with a requests.Response object
|
||||
|
||||
Args:
|
||||
resp_obj (instance): requests.Response instance
|
||||
@@ -64,9 +63,9 @@ class ResponseObject(object):
|
||||
return result
|
||||
else:
|
||||
raise exceptions.ExtractFailure("\tjsonpath {} get nothing\n".format(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.
|
||||
|
||||
Args:
|
||||
@@ -87,15 +86,15 @@ class ResponseObject(object):
|
||||
"""
|
||||
matched = re.search(field, self.text)
|
||||
if not matched:
|
||||
err_msg = u"Failed to extract data with regex! => {}\n".format(field)
|
||||
err_msg += u"response body: {}\n".format(self.text)
|
||||
err_msg = "Failed to extract data with regex! => {}\n".format(field)
|
||||
err_msg += "response body: {}\n".format(self.text)
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.ExtractFailure(err_msg)
|
||||
|
||||
return matched.group(1)
|
||||
|
||||
def _extract_field_with_delimiter(self, field):
|
||||
""" response content could be json or html text.
|
||||
"""response content could be json or html text.
|
||||
|
||||
Args:
|
||||
field (str): string joined by delimiter.
|
||||
@@ -111,7 +110,7 @@ class ResponseObject(object):
|
||||
# string.split(sep=None, maxsplit=1) -> list of strings
|
||||
# e.g. "content.person.name" => ["content", "person.name"]
|
||||
try:
|
||||
top_query, sub_query = field.split('.', 1)
|
||||
top_query, sub_query = field.split(".", 1)
|
||||
except ValueError:
|
||||
top_query = field
|
||||
sub_query = None
|
||||
@@ -120,7 +119,7 @@ class ResponseObject(object):
|
||||
if top_query in ["status_code", "encoding", "ok", "reason", "url"]:
|
||||
if sub_query:
|
||||
# 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)
|
||||
raise exceptions.ParamsError(err_msg)
|
||||
|
||||
@@ -136,16 +135,18 @@ class ResponseObject(object):
|
||||
try:
|
||||
return cookies[sub_query]
|
||||
except KeyError:
|
||||
err_msg = u"Failed to extract cookie! => {}\n".format(field)
|
||||
err_msg += u"response cookies: {}\n".format(cookies)
|
||||
err_msg = "Failed to extract cookie! => {}\n".format(field)
|
||||
err_msg += "response cookies: {}\n".format(cookies)
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.ExtractFailure(err_msg)
|
||||
|
||||
# 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:
|
||||
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
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.ParamsError(err_msg)
|
||||
@@ -154,7 +155,9 @@ class ResponseObject(object):
|
||||
elif sub_query == "total_seconds":
|
||||
return self.elapsed.total_seconds()
|
||||
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
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.ParamsError(err_msg)
|
||||
@@ -169,8 +172,8 @@ class ResponseObject(object):
|
||||
try:
|
||||
return headers[sub_query]
|
||||
except KeyError:
|
||||
err_msg = u"Failed to extract header! => {}\n".format(field)
|
||||
err_msg += u"response headers: {}\n".format(headers)
|
||||
err_msg = "Failed to extract header! => {}\n".format(field)
|
||||
err_msg += "response headers: {}\n".format(headers)
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.ExtractFailure(err_msg)
|
||||
|
||||
@@ -193,8 +196,12 @@ class ResponseObject(object):
|
||||
return utils.query_json(body, sub_query)
|
||||
else:
|
||||
# content = "<html>abcdefg</html>", content.xxx
|
||||
err_msg = u"Failed to extract attribute from response body! => {}\n".format(field)
|
||||
err_msg += u"response body: {}\n".format(body)
|
||||
err_msg = (
|
||||
"Failed to extract attribute from response body! => {}\n".format(
|
||||
field
|
||||
)
|
||||
)
|
||||
err_msg += "response body: {}\n".format(body)
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.ExtractFailure(err_msg)
|
||||
|
||||
@@ -214,26 +221,29 @@ class ResponseObject(object):
|
||||
return utils.query_json(attributes, sub_query)
|
||||
else:
|
||||
# content = "attributes.new_attribute_not_exist"
|
||||
err_msg = u"Failed to extract cumstom set attribute from teardown hooks! => {}\n".format(field)
|
||||
err_msg += u"response set attributes: {}\n".format(attributes)
|
||||
err_msg = "Failed to extract cumstom set attribute from teardown hooks! => {}\n".format(
|
||||
field
|
||||
)
|
||||
err_msg += "response set attributes: {}\n".format(attributes)
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.TeardownHooksFailure(err_msg)
|
||||
|
||||
# others
|
||||
else:
|
||||
err_msg = u"Failed to extract attribute from response! => {}\n".format(field)
|
||||
err_msg += u"available response attributes: status_code, cookies, elapsed, headers, content, " \
|
||||
u"text, json, encoding, ok, reason, url.\n\n"
|
||||
err_msg += u"If you want to set attribute in teardown_hooks, take the following example as reference:\n"
|
||||
err_msg += u"response.new_attribute = 'new_attribute_value'\n"
|
||||
err_msg = "Failed to extract attribute from response! => {}\n".format(field)
|
||||
err_msg += (
|
||||
"available response attributes: status_code, cookies, elapsed, headers, content, "
|
||||
"text, json, encoding, ok, reason, url.\n\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)
|
||||
raise exceptions.ParamsError(err_msg)
|
||||
|
||||
def extract_field(self, field):
|
||||
""" extract value from requests.Response.
|
||||
"""
|
||||
"""extract value from requests.Response."""
|
||||
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)
|
||||
raise exceptions.ParamsError(err_msg)
|
||||
|
||||
@@ -255,7 +265,7 @@ class ResponseObject(object):
|
||||
return value
|
||||
|
||||
def extract_response(self, extractors):
|
||||
""" extract value from requests.Response and store in OrderedDict.
|
||||
"""extract value from requests.Response and store in OrderedDict.
|
||||
|
||||
Args:
|
||||
extractors (list):
|
||||
|
||||
@@ -15,7 +15,7 @@ class HookTypeEnum(Enum):
|
||||
|
||||
|
||||
class Runner(object):
|
||||
""" Running testcases.
|
||||
"""Running testcases.
|
||||
|
||||
Examples:
|
||||
>>> tests_mapping = {
|
||||
@@ -52,7 +52,7 @@ class Runner(object):
|
||||
"""
|
||||
|
||||
def __init__(self, config, http_client_session=None):
|
||||
""" run testcase or testsuite.
|
||||
"""run testcase or testsuite.
|
||||
|
||||
Args:
|
||||
config (dict): testcase/testsuite config dict
|
||||
@@ -87,15 +87,14 @@ class Runner(object):
|
||||
self.do_hook_actions(self.testcase_teardown_hooks, HookTypeEnum.TEARDOWN)
|
||||
|
||||
def __clear_test_data(self):
|
||||
""" clear request and response data
|
||||
"""
|
||||
"""clear request and response data"""
|
||||
if not isinstance(self.http_client_session, HttpSession):
|
||||
return
|
||||
|
||||
self.http_client_session.init_meta_data()
|
||||
|
||||
def _handle_skip_feature(self, test_dict):
|
||||
""" handle skip feature for test
|
||||
"""handle skip feature for test
|
||||
- skip: skip current test unconditionally
|
||||
- skipIf: skip current test if condition is true
|
||||
- skipUnless: skip current test unless condition is true
|
||||
@@ -127,7 +126,7 @@ class Runner(object):
|
||||
raise SkipTest(skip_reason)
|
||||
|
||||
def do_hook_actions(self, actions, hook_type):
|
||||
""" call hook actions.
|
||||
"""call hook actions.
|
||||
|
||||
Args:
|
||||
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
|
||||
)
|
||||
)
|
||||
self.session_context.update_test_variables(
|
||||
var_name, hook_content_eval
|
||||
)
|
||||
self.session_context.update_test_variables(var_name, hook_content_eval)
|
||||
else:
|
||||
# format 2
|
||||
logger.log_debug("call hook function: {}".format(action))
|
||||
@@ -163,7 +160,7 @@ class Runner(object):
|
||||
self.session_context.eval_content(action)
|
||||
|
||||
def _run_test(self, test_dict):
|
||||
""" run single teststep.
|
||||
"""run single teststep.
|
||||
|
||||
Args:
|
||||
test_dict (dict): teststep info
|
||||
@@ -209,7 +206,7 @@ class Runner(object):
|
||||
test_name = self.session_context.eval_content(test_dict.get("name", ""))
|
||||
|
||||
# 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)
|
||||
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)
|
||||
|
||||
# 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", ""))
|
||||
parsed_url = utils.build_url(base_url, url)
|
||||
|
||||
try:
|
||||
method = parsed_test_request.pop('method')
|
||||
method = parsed_test_request.pop("method")
|
||||
parsed_test_request.setdefault("verify", self.verify)
|
||||
group_name = parsed_test_request.pop("group", None)
|
||||
except KeyError:
|
||||
@@ -232,14 +229,12 @@ class Runner(object):
|
||||
|
||||
logger.log_info("{method} {url}".format(method=method, url=parsed_url))
|
||||
logger.log_debug(
|
||||
"request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request))
|
||||
"request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request)
|
||||
)
|
||||
|
||||
# request
|
||||
resp = self.http_client_session.request(
|
||||
method,
|
||||
parsed_url,
|
||||
name=(group_name or test_name),
|
||||
**parsed_test_request
|
||||
method, parsed_url, name=(group_name or test_name), **parsed_test_request
|
||||
)
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
@@ -284,10 +279,7 @@ class Runner(object):
|
||||
validators = test_dict.get("validate") or test_dict.get("validators") or []
|
||||
validate_script = test_dict.get("validate_script", [])
|
||||
if validate_script:
|
||||
validators.append({
|
||||
"type": "python_script",
|
||||
"script": validate_script
|
||||
})
|
||||
validators.append({"type": "python_script", "script": validate_script})
|
||||
|
||||
validator = Validator(self.session_context, resp_obj)
|
||||
try:
|
||||
@@ -299,8 +291,7 @@ class Runner(object):
|
||||
self.validation_results = validator.validation_results
|
||||
|
||||
def _run_testcase(self, testcase_dict):
|
||||
""" run single testcase.
|
||||
"""
|
||||
"""run single testcase."""
|
||||
self.meta_datas = []
|
||||
config = testcase_dict.get("config", {})
|
||||
|
||||
@@ -333,7 +324,7 @@ class Runner(object):
|
||||
)
|
||||
|
||||
def run_test(self, test_dict):
|
||||
""" run single teststep of testcase.
|
||||
"""run single teststep of testcase.
|
||||
test_dict may be in 3 types.
|
||||
|
||||
Args:
|
||||
@@ -370,7 +361,8 @@ class Runner(object):
|
||||
# nested testcase
|
||||
test_dict.setdefault("config", {}).setdefault("variables", {})
|
||||
test_dict["config"]["variables"].update(
|
||||
self.session_context.session_variables_mapping)
|
||||
self.session_context.session_variables_mapping
|
||||
)
|
||||
self._run_testcase(test_dict)
|
||||
else:
|
||||
# api
|
||||
@@ -388,8 +380,7 @@ class Runner(object):
|
||||
self.meta_datas["validators"] = self.validation_results
|
||||
|
||||
def export_variables(self, output_variables_list):
|
||||
""" export current testcase variables
|
||||
"""
|
||||
"""export current testcase variables"""
|
||||
variables_mapping = self.session_context.session_variables_mapping
|
||||
|
||||
output = {}
|
||||
|
||||
@@ -23,7 +23,7 @@ absolute_http_url_regexp = re.compile(r"^https?://", re.I)
|
||||
def init_sentry_sdk():
|
||||
sentry_sdk.init(
|
||||
dsn="https://cc6dd86fbe9f4e7fbd95248cfcff114d@sentry.io/1862849",
|
||||
release="httprunner@{}".format(__version__)
|
||||
release="httprunner@{}".format(__version__),
|
||||
)
|
||||
|
||||
with sentry_sdk.configure_scope() as scope:
|
||||
@@ -32,46 +32,48 @@ def init_sentry_sdk():
|
||||
|
||||
class GAClient(object):
|
||||
|
||||
version = '1' # GA API Version
|
||||
report_url = 'https://www.google-analytics.com/collect'
|
||||
report_debug_url = 'https://www.google-analytics.com/debug/collect' # used for debug
|
||||
version = "1" # GA API Version
|
||||
report_url = "https://www.google-analytics.com/collect"
|
||||
report_debug_url = (
|
||||
"https://www.google-analytics.com/debug/collect" # used for debug
|
||||
)
|
||||
|
||||
def __init__(self, tracking_id):
|
||||
self.http_client = requests.Session()
|
||||
self.label = str(__version__)
|
||||
self.common_params = {
|
||||
'v': self.version,
|
||||
'tid': tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X
|
||||
'cid': uuid.getnode(), # Anonymous Client ID
|
||||
'ua': 'HttpRunner/{}'.format(__version__),
|
||||
"v": self.version,
|
||||
"tid": tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X
|
||||
"cid": uuid.getnode(), # Anonymous Client ID
|
||||
"ua": "HttpRunner/{}".format(__version__),
|
||||
}
|
||||
|
||||
def track_event(self, category, action, value=0):
|
||||
data = {
|
||||
't': 'event', # Event hit type = event
|
||||
'ec': category, # Required. Event Category.
|
||||
'ea': action, # Required. Event Action.
|
||||
'el': self.label, # Optional. Event label, used as version.
|
||||
'ev': value, # Optional. Event value, must be non-negative integer
|
||||
"t": "event", # Event hit type = event
|
||||
"ec": category, # Required. Event Category.
|
||||
"ea": action, # Required. Event Action.
|
||||
"el": self.label, # Optional. Event label, used as version.
|
||||
"ev": value, # Optional. Event value, must be non-negative integer
|
||||
}
|
||||
data.update(self.common_params)
|
||||
try:
|
||||
self.http_client.post(self.report_url, data=data, timeout=5)
|
||||
except Exception: # ProxyError, SSLError, ConnectionError
|
||||
except Exception: # ProxyError, SSLError, ConnectionError
|
||||
pass
|
||||
|
||||
def track_user_timing(self, category, variable, duration):
|
||||
data = {
|
||||
't': 'timing', # Event hit type = timing
|
||||
'utc': category, # Required. user timing category. e.g. jsonLoader
|
||||
'utv': variable, # Required. timing variable. e.g. load
|
||||
'utt': duration, # Required. time took duration.
|
||||
'utl': self.label, # Optional. user timing label, used as version.
|
||||
"t": "timing", # Event hit type = timing
|
||||
"utc": category, # Required. user timing category. e.g. jsonLoader
|
||||
"utv": variable, # Required. timing variable. e.g. load
|
||||
"utt": duration, # Required. time took duration.
|
||||
"utl": self.label, # Optional. user timing label, used as version.
|
||||
}
|
||||
data.update(self.common_params)
|
||||
try:
|
||||
self.http_client.post(self.report_url, data=data, timeout=5)
|
||||
except Exception: # ProxyError, SSLError, ConnectionError
|
||||
except Exception: # ProxyError, SSLError, ConnectionError
|
||||
pass
|
||||
|
||||
|
||||
@@ -79,23 +81,21 @@ ga_client = GAClient("UA-114587036-1")
|
||||
|
||||
|
||||
def set_os_environ(variables_mapping):
|
||||
""" set variables mapping to os.environ
|
||||
"""
|
||||
"""set variables mapping to os.environ"""
|
||||
for variable in variables_mapping:
|
||||
os.environ[variable] = variables_mapping[variable]
|
||||
logger.log_debug("Set OS environment variable: {}".format(variable))
|
||||
|
||||
|
||||
def unset_os_environ(variables_mapping):
|
||||
""" set variables mapping to os.environ
|
||||
"""
|
||||
"""set variables mapping to os.environ"""
|
||||
for variable in variables_mapping:
|
||||
os.environ.pop(variable)
|
||||
logger.log_debug("Unset OS environment variable: {}".format(variable))
|
||||
|
||||
|
||||
def get_os_environ(variable_name):
|
||||
""" get value of environment variable.
|
||||
"""get value of environment variable.
|
||||
|
||||
Args:
|
||||
variable_name(str): variable name
|
||||
@@ -114,7 +114,7 @@ def get_os_environ(variable_name):
|
||||
|
||||
|
||||
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):
|
||||
return path
|
||||
elif base_url:
|
||||
@@ -123,8 +123,8 @@ def build_url(base_url, path):
|
||||
raise ParamsError("base url missed!")
|
||||
|
||||
|
||||
def query_json(json_content, query, delimiter='.'):
|
||||
""" Do an xpath-like query with json_content.
|
||||
def query_json(json_content, query, delimiter="."):
|
||||
"""Do an xpath-like query with json_content.
|
||||
|
||||
Args:
|
||||
json_content (dict/list/string): content to be queried.
|
||||
@@ -158,7 +158,7 @@ def query_json(json_content, query, delimiter='.'):
|
||||
|
||||
"""
|
||||
raise_flag = False
|
||||
response_body = u"response body: {}\n".format(json_content)
|
||||
response_body = "response body: {}\n".format(json_content)
|
||||
try:
|
||||
for key in query.split(delimiter):
|
||||
if isinstance(json_content, (list, basestring)):
|
||||
@@ -167,13 +167,16 @@ def query_json(json_content, query, delimiter='.'):
|
||||
json_content = json_content[key]
|
||||
else:
|
||||
logger.log_error(
|
||||
"invalid type value: {}({})".format(json_content, type(json_content)))
|
||||
"invalid type value: {}({})".format(
|
||||
json_content, type(json_content)
|
||||
)
|
||||
)
|
||||
raise_flag = True
|
||||
except (KeyError, ValueError, IndexError):
|
||||
raise_flag = True
|
||||
|
||||
if raise_flag:
|
||||
err_msg = u"Failed to extract! => {}\n".format(query)
|
||||
err_msg = "Failed to extract! => {}\n".format(query)
|
||||
err_msg += response_body
|
||||
logger.log_error(err_msg)
|
||||
raise exceptions.ExtractFailure(err_msg)
|
||||
@@ -182,7 +185,7 @@ def query_json(json_content, query, delimiter='.'):
|
||||
|
||||
|
||||
def lower_dict_keys(origin_dict):
|
||||
""" convert keys in dict to lower case
|
||||
"""convert keys in dict to lower case
|
||||
|
||||
Args:
|
||||
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):
|
||||
return origin_dict
|
||||
|
||||
return {
|
||||
key.lower(): value
|
||||
for key, value in origin_dict.items()
|
||||
}
|
||||
return {key.lower(): value for key, value in origin_dict.items()}
|
||||
|
||||
|
||||
def lower_test_dict_keys(test_dict):
|
||||
""" convert keys in test_dict to lower case, convertion will occur in two places:
|
||||
1, all keys in test_dict;
|
||||
2, all keys in test_dict["request"]
|
||||
"""convert keys in test_dict to lower case, convertion will occur in two places:
|
||||
1, all keys in test_dict;
|
||||
2, all keys in test_dict["request"]
|
||||
"""
|
||||
# convert keys in test_dict
|
||||
test_dict = lower_dict_keys(test_dict)
|
||||
@@ -235,7 +235,7 @@ def lower_test_dict_keys(test_dict):
|
||||
|
||||
|
||||
def deepcopy_dict(data):
|
||||
""" deepcopy dict data, ignore file object (_io.BufferedReader)
|
||||
"""deepcopy dict data, ignore file object (_io.BufferedReader)
|
||||
|
||||
Args:
|
||||
data (dict): dict data structure
|
||||
@@ -271,7 +271,7 @@ def deepcopy_dict(data):
|
||||
|
||||
|
||||
def ensure_mapping_format(variables):
|
||||
""" ensure variables are in mapping format.
|
||||
"""ensure variables are in mapping format.
|
||||
|
||||
Args:
|
||||
variables (list/dict): original variables
|
||||
@@ -306,7 +306,7 @@ def ensure_mapping_format(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.
|
||||
|
||||
Args:
|
||||
@@ -343,7 +343,7 @@ def extend_variables(raw_variables, override_variables):
|
||||
|
||||
|
||||
def get_testcase_io(testcase):
|
||||
""" get and print testcase input(variables) and output(export).
|
||||
"""get and print testcase input(variables) and output(export).
|
||||
|
||||
Args:
|
||||
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
|
||||
variables = testcase.config.get("variables", {})
|
||||
output_list = testcase.config.get("export") \
|
||||
or testcase.config.get("output", [])
|
||||
output_list = testcase.config.get("export") or testcase.config.get("output", [])
|
||||
export_mapping = test_runner.export_variables(output_list)
|
||||
|
||||
return {
|
||||
"in": variables,
|
||||
"out": export_mapping
|
||||
}
|
||||
return {"in": variables, "out": export_mapping}
|
||||
|
||||
|
||||
def print_info(info_mapping):
|
||||
""" print info in mapping.
|
||||
"""print info in mapping.
|
||||
|
||||
Args:
|
||||
info_mapping (dict): input(variables) or output mapping.
|
||||
@@ -417,10 +413,11 @@ def print_info(info_mapping):
|
||||
|
||||
|
||||
def create_scaffold(project_name):
|
||||
""" create scaffold with specified project name.
|
||||
"""
|
||||
"""create scaffold with specified 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
|
||||
|
||||
ga_client.track_event("Scaffold", "startproject")
|
||||
@@ -433,7 +430,7 @@ def create_scaffold(project_name):
|
||||
logger.color_print(msg, "BLUE")
|
||||
|
||||
def create_file(path, file_content=""):
|
||||
with open(path, 'w') as f:
|
||||
with open(path, "w") as f:
|
||||
f.write(file_content)
|
||||
msg = "created file: {}".format(path)
|
||||
logger.color_print(msg, "BLUE")
|
||||
@@ -498,24 +495,16 @@ testcases:
|
||||
variables:
|
||||
device_sn: $device_sn
|
||||
"""
|
||||
ignore_content = "\n".join([
|
||||
".env",
|
||||
"reports/*",
|
||||
"__pycache__/*",
|
||||
"*.pyc",
|
||||
".python-version",
|
||||
"logs/*"
|
||||
])
|
||||
ignore_content = "\n".join(
|
||||
[".env", "reports/*", "__pycache__/*", "*.pyc", ".python-version", "logs/*"]
|
||||
)
|
||||
demo_debugtalk_content = """
|
||||
import time
|
||||
|
||||
def sleep(n_secs):
|
||||
time.sleep(n_secs)
|
||||
"""
|
||||
demo_env_content = "\n".join([
|
||||
"USERNAME=leolee",
|
||||
"PASSWORD=123456"
|
||||
])
|
||||
demo_env_content = "\n".join(["USERNAME=leolee", "PASSWORD=123456"])
|
||||
|
||||
create_folder(project_name)
|
||||
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, "reports"))
|
||||
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(os.path.join(project_name, "testsuites", "demo_testsuite.yml"), demo_testsuite_content)
|
||||
create_file(
|
||||
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, ".env"), demo_env_content)
|
||||
create_file(os.path.join(project_name, ".gitignore"), ignore_content)
|
||||
|
||||
|
||||
def gen_cartesian_product(*args):
|
||||
""" generate cartesian product for lists
|
||||
"""generate cartesian product for lists
|
||||
|
||||
Args:
|
||||
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):
|
||||
""" prettify JSON testcase format
|
||||
"""
|
||||
"""prettify JSON testcase format"""
|
||||
for json_file in set(file_list):
|
||||
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
|
||||
|
||||
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))
|
||||
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:
|
||||
obj = json.load(stream)
|
||||
except ValueError as e:
|
||||
raise SystemExit(e)
|
||||
|
||||
with io.open(outfile, 'w', encoding='utf-8') as out:
|
||||
json.dump(obj, out, indent=4, separators=(',', ': '))
|
||||
out.write('\n')
|
||||
with io.open(outfile, "w", encoding="utf-8") as out:
|
||||
json.dump(obj, out, indent=4, separators=(",", ": "))
|
||||
out.write("\n")
|
||||
|
||||
print("success: {}".format(outfile))
|
||||
|
||||
|
||||
def omit_long_data(body, omit_len=512):
|
||||
""" omit too long str/bytes
|
||||
"""
|
||||
"""omit too long str/bytes"""
|
||||
if not isinstance(body, basestring):
|
||||
return body
|
||||
|
||||
@@ -618,8 +613,8 @@ def omit_long_data(body, omit_len=512):
|
||||
|
||||
|
||||
def dump_json_file(json_data, json_file_abs_path):
|
||||
""" dump json data to file
|
||||
"""
|
||||
"""dump json data to file"""
|
||||
|
||||
class PythonObjectEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
try:
|
||||
@@ -632,26 +627,28 @@ def dump_json_file(json_data, json_file_abs_path):
|
||||
os.makedirs(file_foder_path)
|
||||
|
||||
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:
|
||||
outfile.write(
|
||||
unicode(json.dumps(
|
||||
json_data,
|
||||
indent=4,
|
||||
separators=(',', ':'),
|
||||
encoding="utf8",
|
||||
ensure_ascii=False,
|
||||
cls=PythonObjectEncoder
|
||||
))
|
||||
unicode(
|
||||
json.dumps(
|
||||
json_data,
|
||||
indent=4,
|
||||
separators=(",", ":"),
|
||||
encoding="utf8",
|
||||
ensure_ascii=False,
|
||||
cls=PythonObjectEncoder,
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
json.dump(
|
||||
json_data,
|
||||
outfile,
|
||||
indent=4,
|
||||
separators=(',', ':'),
|
||||
separators=(",", ":"),
|
||||
ensure_ascii=False,
|
||||
cls=PythonObjectEncoder
|
||||
cls=PythonObjectEncoder,
|
||||
)
|
||||
|
||||
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):
|
||||
""" prepare dump json file absolute path.
|
||||
"""
|
||||
"""prepare dump json file absolute path."""
|
||||
pwd_dir_path = project_mapping.get("PWD") or os.getcwd()
|
||||
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
|
||||
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):
|
||||
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):
|
||||
""" dump tests data to json file.
|
||||
"""dump tests data to json file.
|
||||
the dumped file is located in PWD/logs folder.
|
||||
|
||||
Args:
|
||||
@@ -711,7 +707,11 @@ def get_python2_retire_msg():
|
||||
left_days = (retire_day - today).days
|
||||
|
||||
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:
|
||||
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):
|
||||
""" initialize a Validator for each teststep (API request)
|
||||
"""initialize a Validator for each teststep (API request)
|
||||
|
||||
Args:
|
||||
session_context: HttpRunner session context
|
||||
@@ -27,7 +27,7 @@ class Validator(object):
|
||||
self.validation_results = {}
|
||||
|
||||
def __eval_validator_check(self, check_item):
|
||||
""" evaluate check item in validator.
|
||||
"""evaluate check item in validator.
|
||||
|
||||
Args:
|
||||
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]*"
|
||||
|
||||
"""
|
||||
if isinstance(check_item, (dict, list)) \
|
||||
or isinstance(check_item, parser.LazyString):
|
||||
if isinstance(check_item, (dict, list)) or isinstance(
|
||||
check_item, parser.LazyString
|
||||
):
|
||||
# format 1/2/3
|
||||
check_value = self.session_context.eval_content(check_item)
|
||||
else:
|
||||
@@ -49,7 +50,7 @@ class Validator(object):
|
||||
return check_value
|
||||
|
||||
def __eval_validator_expect(self, expect_item):
|
||||
""" evaluate expect item in validator.
|
||||
"""evaluate expect item in validator.
|
||||
|
||||
Args:
|
||||
expect_item: expect_item should only be in 2 types:
|
||||
@@ -61,12 +62,11 @@ class Validator(object):
|
||||
return expect_value
|
||||
|
||||
def validate_script(self, script):
|
||||
""" make validation with python script
|
||||
"""
|
||||
"""make validation with python script"""
|
||||
result = {
|
||||
"validate_script": "<br/>".join(script),
|
||||
"check_result": "pass",
|
||||
"output": ""
|
||||
"output": "",
|
||||
}
|
||||
|
||||
script = "\n ".join(script)
|
||||
@@ -75,12 +75,14 @@ class Validator(object):
|
||||
|
||||
def run_validate_script():
|
||||
{}
|
||||
""".format(script)
|
||||
""".format(
|
||||
script
|
||||
)
|
||||
|
||||
variables = {
|
||||
"status_code": self.resp_obj.status_code,
|
||||
"response_json": self.resp_obj.json,
|
||||
"response": self.resp_obj
|
||||
"response": self.resp_obj,
|
||||
}
|
||||
variables.update(self.session_context.test_variables_mapping)
|
||||
variables.update(globals())
|
||||
@@ -90,11 +92,13 @@ def run_validate_script():
|
||||
except SyntaxError as ex:
|
||||
logger.log_warning("SyntaxError in python validate script: {}".format(ex))
|
||||
result["check_result"] = "fail"
|
||||
result["output"] = "<br/>".join([
|
||||
"ErrorMessage: {}".format(ex.msg),
|
||||
"ErrorLine: {}".format(ex.lineno),
|
||||
"ErrorText: {}".format(ex.text)
|
||||
])
|
||||
result["output"] = "<br/>".join(
|
||||
[
|
||||
"ErrorMessage: {}".format(ex.msg),
|
||||
"ErrorLine: {}".format(ex.lineno),
|
||||
"ErrorText: {}".format(ex.text),
|
||||
]
|
||||
)
|
||||
return result
|
||||
|
||||
try:
|
||||
@@ -117,16 +121,17 @@ def run_validate_script():
|
||||
else:
|
||||
line_no = "N/A"
|
||||
|
||||
result["output"] = "<br/>".join([
|
||||
"ErrorType: {}".format(_type.__name__),
|
||||
"ErrorLine: {}".format(line_no)
|
||||
])
|
||||
result["output"] = "<br/>".join(
|
||||
[
|
||||
"ErrorType: {}".format(_type.__name__),
|
||||
"ErrorLine: {}".format(line_no),
|
||||
]
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def validate(self, validators):
|
||||
""" make validation with comparators
|
||||
"""
|
||||
"""make validation with comparators"""
|
||||
self.validation_results = {}
|
||||
if not validators:
|
||||
return
|
||||
@@ -154,7 +159,8 @@ def run_validate_script():
|
||||
# validator should be LazyFunction object
|
||||
if not isinstance(validator, parser.LazyFunction):
|
||||
raise exceptions.ValidationFailure(
|
||||
"validator should be parsed first: {}".format(validators))
|
||||
"validator should be parsed first: {}".format(validators)
|
||||
)
|
||||
|
||||
# evaluate validator args with context variable mapping.
|
||||
validator_args = validator.get_args()
|
||||
@@ -169,13 +175,10 @@ def run_validate_script():
|
||||
"check": check_item,
|
||||
"check_value": check_value,
|
||||
"expect": expect_item,
|
||||
"expect_value": expect_value
|
||||
"expect_value": expect_value,
|
||||
}
|
||||
validate_msg = "\nvalidate: {} {} {}({})".format(
|
||||
check_item,
|
||||
comparator,
|
||||
expect_value,
|
||||
type(expect_value).__name__
|
||||
check_item, comparator, expect_value, type(expect_value).__name__
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -192,7 +195,7 @@ def run_validate_script():
|
||||
type(check_value).__name__,
|
||||
comparator,
|
||||
expect_value,
|
||||
type(expect_value).__name__
|
||||
type(expect_value).__name__,
|
||||
)
|
||||
logger.log_error(validate_msg)
|
||||
failures.append(validate_msg)
|
||||
|
||||
@@ -9,6 +9,7 @@ from httprunner.builtin.functions import gen_random_string
|
||||
|
||||
try:
|
||||
from httpbin import app as httpbin_app
|
||||
|
||||
HTTPBIN_HOST = "127.0.0.1"
|
||||
HTTPBIN_PORT = 3458
|
||||
HTTPBIN_SERVER = "http://{}:{}".format(HTTPBIN_HOST, HTTPBIN_PORT)
|
||||
@@ -49,37 +50,30 @@ token_dict = {}
|
||||
|
||||
|
||||
def get_sign(*args):
|
||||
content = ''.join(args).encode('ascii')
|
||||
sign_key = SECRET_KEY.encode('ascii')
|
||||
content = "".join(args).encode("ascii")
|
||||
sign_key = SECRET_KEY.encode("ascii")
|
||||
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
|
||||
return sign
|
||||
|
||||
|
||||
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):
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
device_sn = request.headers.get('device_sn', "")
|
||||
token = request.headers.get('token', "")
|
||||
device_sn = request.headers.get("device_sn", "")
|
||||
token = request.headers.get("token", "")
|
||||
|
||||
if not device_sn or not token:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "device_sn or token is null."
|
||||
}
|
||||
result = {"success": False, "msg": "device_sn or token is null."}
|
||||
response = make_response(json.dumps(result), 401)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
if token_dict[device_sn] != token:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "Authorization failed!"
|
||||
}
|
||||
result = {"success": False, "msg": "Authorization failed!"}
|
||||
response = make_response(json.dumps(result), 403)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
@@ -89,82 +83,64 @@ def validate_request(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
@app.route('/')
|
||||
@app.route("/")
|
||||
def index():
|
||||
return "Hello World!"
|
||||
|
||||
|
||||
@app.route('/api/get-token', methods=['POST'])
|
||||
@app.route("/api/get-token", methods=["POST"])
|
||||
def get_token():
|
||||
device_sn = request.headers.get('device_sn', "")
|
||||
os_platform = request.headers.get('os_platform', "")
|
||||
app_version = request.headers.get('app_version', "")
|
||||
device_sn = request.headers.get("device_sn", "")
|
||||
os_platform = request.headers.get("os_platform", "")
|
||||
app_version = request.headers.get("app_version", "")
|
||||
data = request.get_json()
|
||||
sign = data.get('sign', "")
|
||||
sign = data.get("sign", "")
|
||||
|
||||
expected_sign = get_sign(device_sn, os_platform, app_version)
|
||||
if expected_sign != sign:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "Authorization failed!"
|
||||
}
|
||||
result = {"success": False, "msg": "Authorization failed!"}
|
||||
response = make_response(json.dumps(result), 403)
|
||||
else:
|
||||
token = gen_random_string(16)
|
||||
token_dict[device_sn] = token
|
||||
|
||||
result = {
|
||||
'success': True,
|
||||
'token': token
|
||||
}
|
||||
result = {"success": True, "token": token}
|
||||
response = make_response(json.dumps(result))
|
||||
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/api/users')
|
||||
@app.route("/api/users")
|
||||
@validate_request
|
||||
def get_users():
|
||||
users_list = [user for uid, user in users_dict.items()]
|
||||
users = {
|
||||
'success': True,
|
||||
'count': len(users_list),
|
||||
'items': users_list
|
||||
}
|
||||
users = {"success": True, "count": len(users_list), "items": users_list}
|
||||
response = make_response(json.dumps(users))
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/api/reset-all')
|
||||
@app.route("/api/reset-all")
|
||||
@validate_request
|
||||
def clear_users():
|
||||
users_dict.clear()
|
||||
result = {
|
||||
'success': True
|
||||
}
|
||||
result = {"success": True}
|
||||
response = make_response(json.dumps(result))
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/api/users/<int:uid>', methods=['POST'])
|
||||
@app.route("/api/users/<int:uid>", methods=["POST"])
|
||||
@validate_request
|
||||
def create_user(uid):
|
||||
user = request.get_json()
|
||||
if uid not in users_dict:
|
||||
result = {
|
||||
'success': True,
|
||||
'msg': "user created successfully."
|
||||
}
|
||||
result = {"success": True, "msg": "user created successfully."}
|
||||
status_code = 201
|
||||
users_dict[uid] = user
|
||||
else:
|
||||
result = {
|
||||
'success': False,
|
||||
'msg': "user already existed."
|
||||
}
|
||||
result = {"success": False, "msg": "user already existed."}
|
||||
status_code = 500
|
||||
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
@@ -172,21 +148,15 @@ def create_user(uid):
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/api/users/<int:uid>')
|
||||
@app.route("/api/users/<int:uid>")
|
||||
@validate_request
|
||||
def get_user(uid):
|
||||
user = users_dict.get(uid, {})
|
||||
if user:
|
||||
result = {
|
||||
'success': True,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": True, "data": user}
|
||||
status_code = 200
|
||||
else:
|
||||
result = {
|
||||
'success': False,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": False, "data": user}
|
||||
status_code = 404
|
||||
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
@@ -194,7 +164,7 @@ def get_user(uid):
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/api/users/<int:uid>', methods=['PUT'])
|
||||
@app.route("/api/users/<int:uid>", methods=["PUT"])
|
||||
@validate_request
|
||||
def update_user(uid):
|
||||
user = users_dict.get(uid, {})
|
||||
@@ -207,16 +177,13 @@ def update_user(uid):
|
||||
success = False
|
||||
status_code = 404
|
||||
|
||||
result = {
|
||||
'success': success,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": success, "data": user}
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/api/users/<int:uid>', methods=['DELETE'])
|
||||
@app.route("/api/users/<int:uid>", methods=["DELETE"])
|
||||
@validate_request
|
||||
def delete_user(uid):
|
||||
user = users_dict.pop(uid, {})
|
||||
@@ -227,10 +194,7 @@ def delete_user(uid):
|
||||
success = False
|
||||
status_code = 404
|
||||
|
||||
result = {
|
||||
'success': success,
|
||||
'data': user
|
||||
}
|
||||
result = {"success": success, "data": user}
|
||||
response = make_response(json.dumps(result), status_code)
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
@@ -19,18 +19,13 @@ def run_httpbin():
|
||||
|
||||
|
||||
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
|
||||
def setUpClass(cls):
|
||||
cls.host = "http://127.0.0.1:5000"
|
||||
cls.flask_process = multiprocessing.Process(
|
||||
target=run_flask
|
||||
)
|
||||
cls.httpbin_process = multiprocessing.Process(
|
||||
target=run_httpbin
|
||||
)
|
||||
cls.flask_process = multiprocessing.Process(target=run_flask)
|
||||
cls.httpbin_process = multiprocessing.Process(target=run_httpbin)
|
||||
cls.flask_process.start()
|
||||
cls.httpbin_process.start()
|
||||
time.sleep(1)
|
||||
@@ -44,15 +39,13 @@ class ApiServerUnittest(unittest.TestCase):
|
||||
def get_token(self, user_agent, device_sn, os_platform, app_version):
|
||||
url = "%s/api/get-token" % self.host
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': user_agent,
|
||||
'device_sn': device_sn,
|
||||
'os_platform': os_platform,
|
||||
'app_version': app_version
|
||||
}
|
||||
data = {
|
||||
'sign': get_sign(device_sn, os_platform, app_version)
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": user_agent,
|
||||
"device_sn": device_sn,
|
||||
"os_platform": os_platform,
|
||||
"app_version": app_version,
|
||||
}
|
||||
data = {"sign": get_sign(device_sn, os_platform, app_version)}
|
||||
|
||||
resp = self.api_client.post(url, json=data, headers=headers)
|
||||
resp_json = resp.json()
|
||||
@@ -62,14 +55,11 @@ class ApiServerUnittest(unittest.TestCase):
|
||||
return resp_json["token"]
|
||||
|
||||
def get_authenticated_headers(self):
|
||||
user_agent = 'iOS/10.3'
|
||||
user_agent = "iOS/10.3"
|
||||
device_sn = gen_random_string(15)
|
||||
os_platform = 'ios'
|
||||
app_version = '2.8.6'
|
||||
os_platform = "ios"
|
||||
app_version = "2.8.6"
|
||||
|
||||
token = self.get_token(user_agent, device_sn, os_platform, app_version)
|
||||
headers = {
|
||||
'device_sn': device_sn,
|
||||
'token': token
|
||||
}
|
||||
headers = {"device_sn": device_sn, "token": token}
|
||||
return headers
|
||||
|
||||
@@ -17,12 +17,7 @@ def get_base_url():
|
||||
|
||||
|
||||
def get_default_request():
|
||||
return {
|
||||
"base_url": BASE_URL,
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
}
|
||||
return {"base_url": BASE_URL, "headers": {"content-type": "application/json"}}
|
||||
|
||||
|
||||
def sum_two(m, n):
|
||||
@@ -30,8 +25,8 @@ def sum_two(m, n):
|
||||
|
||||
|
||||
def sum_status_code(status_code, expect_sum):
|
||||
""" sum status code digits
|
||||
e.g. 400 => 4, 201 => 3
|
||||
"""sum status code digits
|
||||
e.g. 400 => 4, 201 => 3
|
||||
"""
|
||||
sum_value = 0
|
||||
for digit in str(status_code):
|
||||
@@ -48,8 +43,7 @@ os.environ["TEST_ENV"] = "PRODUCTION"
|
||||
|
||||
|
||||
def skip_test_in_production_env():
|
||||
""" skip this test in production environment
|
||||
"""
|
||||
"""skip this test in production environment"""
|
||||
return os.environ["TEST_ENV"] == "PRODUCTION"
|
||||
|
||||
|
||||
@@ -58,16 +52,13 @@ def get_user_agent():
|
||||
|
||||
|
||||
def gen_app_version():
|
||||
return [
|
||||
{"app_version": "2.8.5"},
|
||||
{"app_version": "2.8.6"}
|
||||
]
|
||||
return [{"app_version": "2.8.5"}, {"app_version": "2.8.6"}]
|
||||
|
||||
|
||||
def get_account():
|
||||
return [
|
||||
{"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_list.append(random_char)
|
||||
|
||||
random_string = ''.join(random_char_list)
|
||||
random_string = "".join(random_char_list)
|
||||
return random_string
|
||||
|
||||
|
||||
@@ -94,8 +85,7 @@ def setup_hook_remove_kwargs(request):
|
||||
|
||||
|
||||
def teardown_hook_sleep_N_secs(response, n_secs):
|
||||
""" sleep n seconds after request
|
||||
"""
|
||||
"""sleep n seconds after request"""
|
||||
if response.status_code == 200:
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
@@ -113,9 +103,11 @@ def modify_request_json(request, os_platform):
|
||||
def setup_hook_httpntlmauth(request):
|
||||
if "httpntlmauth" in request:
|
||||
from requests_ntlm import HttpNtlmAuth
|
||||
|
||||
auth_account = request.pop("httpntlmauth")
|
||||
request["auth"] = HttpNtlmAuth(
|
||||
auth_account["username"], auth_account["password"])
|
||||
auth_account["username"], auth_account["password"]
|
||||
)
|
||||
|
||||
|
||||
def alter_response(response):
|
||||
@@ -123,18 +115,15 @@ def alter_response(response):
|
||||
response.headers["Content-Type"] = "html/text"
|
||||
response.json["headers"]["Host"] = "127.0.0.1:8888"
|
||||
response.new_attribute = "new_attribute_value"
|
||||
response.new_attribute_dict = {
|
||||
"key": 123
|
||||
}
|
||||
response.new_attribute_dict = {"key": 123}
|
||||
|
||||
|
||||
def alter_response_302(response):
|
||||
response.status_code = 500
|
||||
response.headers["Content-Type"] = "html/text"
|
||||
response.text = "abcdef"
|
||||
response.new_attribute = "new_attribute_value"
|
||||
response.new_attribute_dict = {
|
||||
"key": 123
|
||||
}
|
||||
response.new_attribute_dict = {"key": 123}
|
||||
|
||||
|
||||
def alter_response_error(response):
|
||||
@@ -143,7 +132,4 @@ def alter_response_error(response):
|
||||
|
||||
|
||||
def gen_variables():
|
||||
return {
|
||||
"var_a": 1,
|
||||
"var_b": 2
|
||||
}
|
||||
return {"var_a": 1, "var_b": 2}
|
||||
|
||||
@@ -11,64 +11,69 @@ from tests.base import ApiServerUnittest
|
||||
|
||||
|
||||
class TestHttpRunner(ApiServerUnittest):
|
||||
|
||||
def setUp(self):
|
||||
self.testcase_cli_path = "tests/data/demo_testcase_cli.yml"
|
||||
self.testcase_file_path_list = [
|
||||
os.path.join(
|
||||
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.yml"),
|
||||
os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.json"),
|
||||
]
|
||||
testcases = [{
|
||||
'config': {
|
||||
'name': 'testcase description',
|
||||
'request': {
|
||||
'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'}
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
"name": "testcase description",
|
||||
"request": {
|
||||
"base_url": "",
|
||||
"headers": {"User-Agent": "python-requests/2.18.4"},
|
||||
},
|
||||
'extract': [
|
||||
{'token': 'content.token'}
|
||||
],
|
||||
'validate': [
|
||||
{'eq': ['status_code', 200]},
|
||||
{'eq': ['headers.Content-Type', 'application/json']},
|
||||
{'eq': ['content.success', True]}
|
||||
]
|
||||
"variables": [],
|
||||
},
|
||||
{
|
||||
'name': '/api/users/1000',
|
||||
'request': {
|
||||
'url': 'http://127.0.0.1:5000/api/users/1000',
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json',
|
||||
'device_sn': 'FwgRiO7CNA50DSU','token': '$token'},
|
||||
'json': {'name': 'user1', 'password': '123456'}
|
||||
"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": [{"token": "content.token"}],
|
||||
"validate": [
|
||||
{"eq": ["status_code", 200]},
|
||||
{"eq": ["headers.Content-Type", "application/json"]},
|
||||
{"eq": ["content.success", True]},
|
||||
],
|
||||
},
|
||||
'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
|
||||
}
|
||||
{
|
||||
"name": "/api/users/1000",
|
||||
"request": {
|
||||
"url": "http://127.0.0.1:5000/api/users/1000",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"device_sn": "FwgRiO7CNA50DSU",
|
||||
"token": "$token",
|
||||
},
|
||||
"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.reset_all()
|
||||
|
||||
@@ -85,10 +90,7 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
def test_text_run_times_invalid(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
'name': "post data",
|
||||
'variables': []
|
||||
},
|
||||
"config": {"name": "post data", "variables": []},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "post data",
|
||||
@@ -98,20 +100,16 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"User-Agent": "python-requests/2.18.4",
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"data": "abc"
|
||||
"data": "abc",
|
||||
},
|
||||
"validate": [
|
||||
{"eq": ["status_code", 200]}
|
||||
]
|
||||
"validate": [{"eq": ["status_code", 200]}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"testcases": testcases
|
||||
}
|
||||
tests_mapping = {"testcases": testcases}
|
||||
with self.assertRaises(exceptions.ParamsError):
|
||||
self.runner.run_tests(tests_mapping)
|
||||
|
||||
@@ -123,12 +121,9 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
'name': "post data",
|
||||
'variables': {
|
||||
"var1": "abc",
|
||||
"var2": "def"
|
||||
},
|
||||
"export": ["status_code", "req_data"]
|
||||
"name": "post data",
|
||||
"variables": {"var1": "abc", "var2": "def"},
|
||||
"export": ["status_code", "req_data"],
|
||||
},
|
||||
"teststeps": [
|
||||
{
|
||||
@@ -138,24 +133,20 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"User-Agent": "python-requests/2.18.4",
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"data": "$var1"
|
||||
"data": "$var1",
|
||||
},
|
||||
"extract": {
|
||||
"status_code": "status_code",
|
||||
"req_data": "content.data"
|
||||
"req_data": "content.data",
|
||||
},
|
||||
"validate": [
|
||||
{"eq": ["status_code", 200]}
|
||||
]
|
||||
"validate": [{"eq": ["status_code", 200]}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"testcases": testcases
|
||||
}
|
||||
tests_mapping = {"testcases": testcases}
|
||||
self.runner.run_tests(tests_mapping)
|
||||
vars_out = self.runner.get_vars_out()
|
||||
self.assertIsInstance(vars_out, list)
|
||||
@@ -166,7 +157,8 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
|
||||
def test_save_variables_output_with_parameters(self):
|
||||
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)
|
||||
vars_out = self.runner.get_vars_out()
|
||||
self.assertIsInstance(vars_out, list)
|
||||
@@ -188,7 +180,7 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 10)
|
||||
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)
|
||||
self.assertGreater(len(os.listdir(report_save_dir)), 0)
|
||||
shutil.rmtree(report_save_dir)
|
||||
@@ -200,7 +192,7 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 10)
|
||||
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_save_dir = os.path.dirname(report_file)
|
||||
self.assertEqual(len(os.listdir(report_save_dir)), 1)
|
||||
@@ -208,7 +200,7 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
shutil.rmtree(report_save_dir)
|
||||
|
||||
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.run(self.testcase_cli_path)
|
||||
self.assertTrue(os.path.isfile(log_file_path))
|
||||
@@ -223,10 +215,7 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
self.assertIn("records", summary["details"][0])
|
||||
|
||||
def test_run_yaml_upload(self):
|
||||
upload_cases_list = [
|
||||
"tests/httpbin/upload.yml",
|
||||
"tests/httpbin/upload.v2.yml"
|
||||
]
|
||||
upload_cases_list = ["tests/httpbin/upload.yml", "tests/httpbin/upload.v2.yml"]
|
||||
for upload_case in upload_cases_list:
|
||||
summary = self.runner.run(upload_case)
|
||||
self.assertTrue(summary["success"])
|
||||
@@ -238,10 +227,7 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
def test_run_post_data(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
'name': "post data",
|
||||
'variables': []
|
||||
},
|
||||
"config": {"name": "post data", "variables": []},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "post data",
|
||||
@@ -250,35 +236,32 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"User-Agent": "python-requests/2.18.4",
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"data": "abc"
|
||||
"data": "abc",
|
||||
},
|
||||
"validate": [
|
||||
{"eq": ["status_code", 200]}
|
||||
]
|
||||
"validate": [{"eq": ["status_code", 200]}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"testcases": testcases
|
||||
}
|
||||
tests_mapping = {"testcases": testcases}
|
||||
summary = self.runner.run_tests(tests_mapping)
|
||||
self.assertTrue(summary["success"])
|
||||
self.assertEqual(summary["stat"]["testcases"]["total"], 1)
|
||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 1)
|
||||
resp_json = json.loads(summary["details"][0]["records"][0]["meta_datas"]["data"][0]["response"]["body"])
|
||||
self.assertEqual(
|
||||
resp_json["data"],
|
||||
"abc"
|
||||
resp_json = json.loads(
|
||||
summary["details"][0]["records"][0]["meta_datas"]["data"][0]["response"][
|
||||
"body"
|
||||
]
|
||||
)
|
||||
self.assertEqual(resp_json["data"], "abc")
|
||||
|
||||
def test_html_report_repsonse_image(self):
|
||||
runner = HttpRunner(failfast=True)
|
||||
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)
|
||||
self.assertTrue(os.path.isfile(report_path))
|
||||
shutil.rmtree(report_save_dir)
|
||||
@@ -286,7 +269,9 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
def test_testcase_layer_with_api(self):
|
||||
summary = self.runner.run("tests/testcases/setup.yml")
|
||||
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"]["teststeps"]["total"], 2)
|
||||
|
||||
@@ -301,8 +286,7 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
self.assertFalse(summary["success"])
|
||||
|
||||
def test_run_httprunner_with_hooks(self):
|
||||
testcase_file_path = os.path.join(
|
||||
os.getcwd(), 'tests/httpbin/hooks.yml')
|
||||
testcase_file_path = os.path.join(os.getcwd(), "tests/httpbin/hooks.yml")
|
||||
start_time = time.time()
|
||||
summary = self.runner.run(testcase_file_path)
|
||||
end_time = time.time()
|
||||
@@ -319,11 +303,9 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
"request": {
|
||||
"url": "{}/headers".format(HTTPBIN_SERVER),
|
||||
"method": "GET",
|
||||
"data": "abc"
|
||||
"data": "abc",
|
||||
},
|
||||
"teardown_hooks": [
|
||||
"${alter_response($response)}"
|
||||
],
|
||||
"teardown_hooks": ["${alter_response($response)}"],
|
||||
"validate": [
|
||||
{"eq": ["status_code", 500]},
|
||||
{"eq": ["headers.content-type", "html/text"]},
|
||||
@@ -332,16 +314,16 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
{"eq": ["text.headers.Host", "127.0.0.1:8888"]},
|
||||
{"eq": ["new_attribute", "new_attribute_value"]},
|
||||
{"eq": ["new_attribute_dict", {"key": 123}]},
|
||||
{"eq": ["new_attribute_dict.key", 123]}
|
||||
]
|
||||
{"eq": ["new_attribute_dict.key", 123]},
|
||||
],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
tests_mapping = {
|
||||
"project_mapping": loader.load_project_data("tests"),
|
||||
"testcases": testcases
|
||||
"testcases": testcases,
|
||||
}
|
||||
summary = self.runner.run_tests(tests_mapping)
|
||||
self.assertTrue(summary["success"])
|
||||
@@ -349,30 +331,24 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
def test_run_httprunner_with_teardown_hooks_not_exist_attribute(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
"name": "test teardown hooks"
|
||||
},
|
||||
"config": {"name": "test teardown hooks"},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "test teardown hooks",
|
||||
"request": {
|
||||
"url": "{}/headers".format(HTTPBIN_SERVER),
|
||||
"method": "GET",
|
||||
"data": "abc"
|
||||
"data": "abc",
|
||||
},
|
||||
"teardown_hooks": [
|
||||
"${alter_response($response)}"
|
||||
],
|
||||
"validate": [
|
||||
{"eq": ["attribute_not_exist", "new_attribute"]}
|
||||
]
|
||||
"teardown_hooks": ["${alter_response($response)}"],
|
||||
"validate": [{"eq": ["attribute_not_exist", "new_attribute"]}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"project_mapping": loader.load_project_data("tests"),
|
||||
"testcases": testcases
|
||||
"testcases": testcases,
|
||||
}
|
||||
summary = self.runner.run_tests(tests_mapping)
|
||||
self.assertFalse(summary["success"])
|
||||
@@ -381,27 +357,23 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
def test_run_httprunner_with_teardown_hooks_error(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
"name": "test teardown hooks"
|
||||
},
|
||||
"config": {"name": "test teardown hooks"},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "test teardown hooks",
|
||||
"request": {
|
||||
"url": "{}/headers".format(HTTPBIN_SERVER),
|
||||
"method": "GET",
|
||||
"data": "abc"
|
||||
"data": "abc",
|
||||
},
|
||||
"teardown_hooks": [
|
||||
"${alter_response_error($response)}"
|
||||
]
|
||||
"teardown_hooks": ["${alter_response_error($response)}"],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"project_mapping": loader.load_project_data("tests"),
|
||||
"testcases": testcases
|
||||
"testcases": testcases,
|
||||
}
|
||||
summary = self.runner.run_tests(tests_mapping)
|
||||
self.assertFalse(summary["success"])
|
||||
@@ -478,29 +450,32 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 3)
|
||||
self.assertEqual(summary["stat"]["teststeps"]["successes"], 3)
|
||||
|
||||
|
||||
def test_run_testcase_template_variables(self):
|
||||
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)
|
||||
self.assertTrue(summary["success"])
|
||||
|
||||
def test_run_testcase_template_import_functions(self):
|
||||
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)
|
||||
self.assertTrue(summary["success"])
|
||||
|
||||
def test_run_testcase_layered(self):
|
||||
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)
|
||||
self.assertTrue(summary["success"])
|
||||
self.assertEqual(len(summary["details"]), 1)
|
||||
|
||||
def test_run_testcase_output(self):
|
||||
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)
|
||||
self.assertTrue(summary["success"])
|
||||
self.assertIn("token", summary["details"][0]["in_out"]["out"])
|
||||
@@ -509,10 +484,9 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
|
||||
def test_run_testcase_with_variables_mapping(self):
|
||||
testcase_file_path = os.path.join(
|
||||
os.getcwd(), 'tests/data/demo_testcase_layer.yml')
|
||||
variables_mapping = {
|
||||
"app_version": '2.9.7'
|
||||
}
|
||||
os.getcwd(), "tests/data/demo_testcase_layer.yml"
|
||||
)
|
||||
variables_mapping = {"app_version": "2.9.7"}
|
||||
summary = self.runner.run(testcase_file_path, mapping=variables_mapping)
|
||||
self.assertTrue(summary["success"])
|
||||
self.assertIn("token", summary["details"][0]["in_out"]["out"])
|
||||
@@ -521,7 +495,8 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
|
||||
def test_run_testcase_with_parameters(self):
|
||||
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)
|
||||
self.assertTrue(summary["success"])
|
||||
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["details"][0]["name"],
|
||||
"create user 101 and check result for TESTSUITE_X1."
|
||||
"create user 101 and check result for TESTSUITE_X1.",
|
||||
)
|
||||
self.assertEqual(
|
||||
summary["details"][5]["name"],
|
||||
"create user 103 and check result for TESTSUITE_X2."
|
||||
)
|
||||
self.assertEqual(
|
||||
summary["details"][0]["stat"]["total"],
|
||||
2
|
||||
"create user 103 and check result for TESTSUITE_X2.",
|
||||
)
|
||||
self.assertEqual(summary["details"][0]["stat"]["total"], 2)
|
||||
records_name_list = [
|
||||
summary["details"][i]["records"][1]["meta_datas"][1]["name"]
|
||||
for i in range(6)
|
||||
@@ -552,59 +524,51 @@ class TestHttpRunner(ApiServerUnittest):
|
||||
"create user 102 for TESTSUITE_X1",
|
||||
"create user 102 for TESTSUITE_X2",
|
||||
"create user 103 for TESTSUITE_X1",
|
||||
"create user 103 for TESTSUITE_X2"
|
||||
}
|
||||
"create user 103 for TESTSUITE_X2",
|
||||
},
|
||||
)
|
||||
|
||||
def test_validate_response_content(self):
|
||||
testcase_file_path = os.path.join(
|
||||
os.getcwd(), 'tests/httpbin/basic.yml')
|
||||
testcase_file_path = os.path.join(os.getcwd(), "tests/httpbin/basic.yml")
|
||||
summary = self.runner.run(testcase_file_path)
|
||||
self.assertTrue(summary["success"])
|
||||
|
||||
def test_html_report_xss(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
'name': "post data"
|
||||
},
|
||||
"config": {"name": "post data"},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "post data",
|
||||
"request": {
|
||||
"url": "{}/anything".format(HTTPBIN_SERVER),
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"headers": {"Content-Type": "application/json"},
|
||||
"json": {
|
||||
'success': False,
|
||||
"person": "<img src=x onerror=alert(1)>"
|
||||
}
|
||||
"success": False,
|
||||
"person": "<img src=x onerror=alert(1)>",
|
||||
},
|
||||
},
|
||||
"validate": [
|
||||
{"eq": ["status_code", 200]}
|
||||
]
|
||||
"validate": [{"eq": ["status_code", 200]}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"testcases": testcases
|
||||
}
|
||||
tests_mapping = {"testcases": testcases}
|
||||
summary = self.runner.run(tests_mapping)
|
||||
report_path = report.gen_html_report(summary)
|
||||
with open(report_path) as f:
|
||||
content = f.read()
|
||||
m = re.findall(
|
||||
re.escape(""person": "<img src=x onerror=alert(1)>""),
|
||||
content
|
||||
re.escape(
|
||||
""person": "<img src=x onerror=alert(1)>""
|
||||
),
|
||||
content,
|
||||
)
|
||||
self.assertEqual(len(m), 2)
|
||||
|
||||
|
||||
class TestApi(ApiServerUnittest):
|
||||
|
||||
def test_testcase_loader(self):
|
||||
testcase_path = "tests/testcases/setup.yml"
|
||||
tests_mapping = loader.load_cases(testcase_path)
|
||||
@@ -628,7 +592,9 @@ class TestApi(ApiServerUnittest):
|
||||
self.assertEqual(testcase_tests[0]["name"], "get token (setup)")
|
||||
self.assertIsInstance(testcase_tests[0]["variables"], dict)
|
||||
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):
|
||||
testcase_path = "tests/testcases/setup.yml"
|
||||
@@ -696,7 +662,7 @@ class TestApi(ApiServerUnittest):
|
||||
"tests/testcases/create_user.yml",
|
||||
"tests/testcases/create_user.v2.yml",
|
||||
"tests/testcases/create_user.json",
|
||||
"tests/testcases/create_user.v2.json"
|
||||
"tests/testcases/create_user.v2.json",
|
||||
]:
|
||||
tests_mapping = loader.load_cases(testcase_path)
|
||||
testcases = parser.parse_tests(tests_mapping)
|
||||
@@ -708,11 +674,10 @@ class TestApi(ApiServerUnittest):
|
||||
results = tests_results[0][1]
|
||||
self.assertEqual(
|
||||
results.records[0]["name"],
|
||||
"setup and reset all (override) for TESTCASE_CREATE_XXX."
|
||||
"setup and reset all (override) for TESTCASE_CREATE_XXX.",
|
||||
)
|
||||
self.assertEqual(
|
||||
results.records[1]["name"],
|
||||
"create user and check result."
|
||||
results.records[1]["name"], "create user and check result."
|
||||
)
|
||||
|
||||
def test_testsuite_loader(self):
|
||||
@@ -740,11 +705,14 @@ class TestApi(ApiServerUnittest):
|
||||
self.assertIn("testcase_def", testcase_tests)
|
||||
self.assertEqual(testcase_tests["name"], "create user 1000 and check result.")
|
||||
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(
|
||||
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):
|
||||
@@ -756,14 +724,13 @@ class TestApi(ApiServerUnittest):
|
||||
self.assertEqual(len(parsed_testcases[0]["teststeps"]), 2)
|
||||
|
||||
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"]
|
||||
self.assertNotIn("testcase_def", testcase1)
|
||||
self.assertEqual(len(teststeps), 2)
|
||||
self.assertEqual(
|
||||
teststeps[0]["request"]["url"],
|
||||
"/api/get-token"
|
||||
)
|
||||
self.assertEqual(teststeps[0]["request"]["url"], "/api/get-token")
|
||||
|
||||
def test_testsuite_add_tests(self):
|
||||
testcase_path = "tests/testsuites/create_users.yml"
|
||||
@@ -775,7 +742,9 @@ class TestApi(ApiServerUnittest):
|
||||
|
||||
self.assertEqual(len(test_suite._tests), 2)
|
||||
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):
|
||||
testcase_path = "tests/testsuites/create_users.yml"
|
||||
@@ -790,11 +759,5 @@ class TestApi(ApiServerUnittest):
|
||||
self.assertEqual(len(tests_results[0][1].records), 2)
|
||||
|
||||
results = tests_results[0][1]
|
||||
self.assertIn(
|
||||
"setup and reset all (override)",
|
||||
results.records[0]["name"]
|
||||
)
|
||||
self.assertEqual(
|
||||
results.records[1]["name"],
|
||||
"create user and check result."
|
||||
)
|
||||
self.assertIn("setup and reset all (override)", 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):
|
||||
|
||||
def setUp(self):
|
||||
super(TestApiServer, self).setUp()
|
||||
self.headers = self.get_authenticated_headers()
|
||||
@@ -25,10 +24,7 @@ class TestApiServer(ApiServerUnittest):
|
||||
|
||||
def create_user(self, uid, name, password):
|
||||
url = "%s/api/users/%d" % (self.host, uid)
|
||||
data = {
|
||||
'name': name,
|
||||
'password': password
|
||||
}
|
||||
data = {"name": name, "password": password}
|
||||
return self.api_client.post(url, headers=self.headers, json=data)
|
||||
|
||||
def get_user(self, uid):
|
||||
@@ -37,10 +33,7 @@ class TestApiServer(ApiServerUnittest):
|
||||
|
||||
def update_user(self, uid, name, password):
|
||||
url = "%s/api/users/%d" % (self.host, uid)
|
||||
data = {
|
||||
'name': name,
|
||||
'password': password
|
||||
}
|
||||
data = {"name": name, "password": password}
|
||||
return self.api_client.put(url, headers=self.headers, json=data)
|
||||
|
||||
def delete_user(self, uid):
|
||||
@@ -50,63 +43,63 @@ class TestApiServer(ApiServerUnittest):
|
||||
def test_reset_all(self):
|
||||
resp = self.reset_all()
|
||||
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):
|
||||
resp = self.create_user(1000, 'user1', '123456')
|
||||
resp = self.create_user(1000, "user1", "123456")
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assertEqual(True, resp.json()['success'])
|
||||
self.assertEqual(True, resp.json()["success"])
|
||||
|
||||
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)
|
||||
|
||||
def test_get_users_empty(self):
|
||||
resp = self.get_users()
|
||||
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):
|
||||
resp = self.create_user(1000, 'user1', '123456')
|
||||
resp = self.create_user(1000, "user1", "123456")
|
||||
resp = self.get_users()
|
||||
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()
|
||||
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):
|
||||
resp = self.get_user(1000)
|
||||
self.assertEqual(404, resp.status_code)
|
||||
self.assertEqual(resp.json()['success'], False)
|
||||
self.assertEqual(resp.json()["success"], False)
|
||||
|
||||
def test_get_user_existed(self):
|
||||
self.create_user(1000, 'user1', '123456')
|
||||
self.create_user(1000, "user1", "123456")
|
||||
resp = self.get_user(1000)
|
||||
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):
|
||||
resp = self.update_user(1000, 'user1', '123456')
|
||||
resp = self.update_user(1000, "user1", "123456")
|
||||
self.assertEqual(404, resp.status_code)
|
||||
self.assertEqual(resp.json()['success'], False)
|
||||
self.assertEqual(resp.json()["success"], False)
|
||||
|
||||
def test_update_user_existed(self):
|
||||
self.create_user(1000, 'user1', '123456')
|
||||
resp = self.update_user(1000, 'user2', '123456')
|
||||
self.create_user(1000, "user1", "123456")
|
||||
resp = self.update_user(1000, "user2", "123456")
|
||||
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):
|
||||
resp = self.delete_user(1000)
|
||||
self.assertEqual(404, resp.status_code)
|
||||
self.assertEqual(resp.json()['success'], False)
|
||||
self.assertEqual(resp.json()["success"], False)
|
||||
|
||||
def test_delete_user_existed(self):
|
||||
self.create_user(1000, 'leo', '123456')
|
||||
self.create_user(1000, "leo", "123456")
|
||||
resp = self.delete_user(1000)
|
||||
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):
|
||||
|
||||
def setUp(self):
|
||||
self.captured_output = io.StringIO()
|
||||
sys.stdout = self.captured_output
|
||||
@@ -23,6 +22,7 @@ class TestCli(unittest.TestCase):
|
||||
self.assertEqual(cm.exception.code, 0)
|
||||
|
||||
from httprunner import __version__
|
||||
|
||||
self.assertIn(__version__, self.captured_output.getvalue().strip())
|
||||
|
||||
def test_show_help(self):
|
||||
@@ -34,4 +34,5 @@ class TestCli(unittest.TestCase):
|
||||
self.assertEqual(cm.exception.code, 0)
|
||||
|
||||
from httprunner import __description__
|
||||
|
||||
self.assertIn(__description__, self.captured_output.getvalue().strip())
|
||||
|
||||
@@ -20,30 +20,21 @@ class TestHttpClient(ApiServerUnittest):
|
||||
|
||||
def test_request_with_full_url(self):
|
||||
url = "%s/api/users/1000" % self.host
|
||||
data = {
|
||||
'name': 'user1',
|
||||
'password': '123456'
|
||||
}
|
||||
data = {"name": "user1", "password": "123456"}
|
||||
resp = self.api_client.post(url, json=data, headers=self.headers)
|
||||
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):
|
||||
url = "{}/api/users/1000".format(self.host)
|
||||
data = {
|
||||
'name': 'user1',
|
||||
'password': '123456'
|
||||
}
|
||||
data = {"name": "user1", "password": "123456"}
|
||||
resp = self.api_client.post(url, json=data, headers=self.headers)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assertEqual(True, resp.json()['success'])
|
||||
self.assertEqual(True, resp.json()["success"])
|
||||
|
||||
def test_request_post_data(self):
|
||||
url = "{}/api/users/1000".format(self.host)
|
||||
data = {
|
||||
'name': 'user1',
|
||||
'password': '123456'
|
||||
}
|
||||
data = {"name": "user1", "password": "123456"}
|
||||
resp = self.api_client.post(url, json=data, headers=self.headers)
|
||||
# b'{"name": "user1", "password": "123456"}'
|
||||
self.assertIn(b'"name": "user1"', resp.request.body)
|
||||
@@ -56,14 +47,8 @@ class TestHttpClient(ApiServerUnittest):
|
||||
|
||||
def test_request_with_cookies(self):
|
||||
url = "{}/api/users/1000".format(self.host)
|
||||
data = {
|
||||
'name': 'user1',
|
||||
'password': '123456'
|
||||
}
|
||||
cookies = {
|
||||
"a": "1",
|
||||
"b": "2"
|
||||
}
|
||||
data = {"name": "user1", "password": "123456"}
|
||||
cookies = {"a": "1", "b": "2"}
|
||||
resp = self.api_client.get(url, cookies=cookies, headers=self.headers)
|
||||
self.assertEqual(resp.request._cookies["a"], "1")
|
||||
self.assertEqual(resp.request._cookies["b"], "2")
|
||||
|
||||
@@ -6,17 +6,13 @@ from tests.base import ApiServerUnittest, gen_random_string
|
||||
|
||||
|
||||
class TestContext(ApiServerUnittest):
|
||||
|
||||
def setUp(self):
|
||||
loader.load_project_data(os.path.join(os.getcwd(), "tests"))
|
||||
self.context = context.SessionContext(
|
||||
variables={"SECRET_KEY": "DebugTalk"}
|
||||
)
|
||||
self.context = context.SessionContext(variables={"SECRET_KEY": "DebugTalk"})
|
||||
|
||||
def test_init_test_variables_initialize(self):
|
||||
self.assertEqual(
|
||||
self.context.test_variables_mapping,
|
||||
{'SECRET_KEY': 'DebugTalk'}
|
||||
self.context.test_variables_mapping, {"SECRET_KEY": "DebugTalk"}
|
||||
)
|
||||
|
||||
def test_init_test_variables(self):
|
||||
@@ -28,36 +24,25 @@ class TestContext(ApiServerUnittest):
|
||||
# "data": '{"name": "$username", "password": "123456"}',
|
||||
"TOKEN": "debugtalk",
|
||||
"username": "user1",
|
||||
"num": 6
|
||||
}
|
||||
functions = {
|
||||
"gen_random_string": gen_random_string,
|
||||
"gen_md5": gen_md5
|
||||
"num": 6,
|
||||
}
|
||||
functions = {"gen_random_string": gen_random_string, "gen_md5": gen_md5}
|
||||
variables = parser.prepare_lazy_data(variables, functions, variables.keys())
|
||||
variables = parser.parse_variables_mapping(variables)
|
||||
self.context.init_test_variables(variables)
|
||||
variables_mapping = self.context.test_variables_mapping
|
||||
self.assertEqual(len(variables_mapping["random"]), 6)
|
||||
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):
|
||||
self.context.update_session_variables({"TOKEN": "debugtalk"})
|
||||
self.assertEqual(
|
||||
self.context.session_variables_mapping["TOKEN"],
|
||||
"debugtalk"
|
||||
)
|
||||
self.assertEqual(self.context.session_variables_mapping["TOKEN"], "debugtalk")
|
||||
|
||||
def test_eval_content_variables(self):
|
||||
variables = {
|
||||
"SECRET_KEY": "DebugTalk"
|
||||
}
|
||||
variables = {"SECRET_KEY": "DebugTalk"}
|
||||
content = parser.prepare_lazy_data("abc$SECRET_KEY", {}, variables.keys())
|
||||
self.assertEqual(
|
||||
self.context.eval_content(content),
|
||||
"abcDebugTalk"
|
||||
)
|
||||
self.assertEqual(self.context.eval_content(content), "abcDebugTalk")
|
||||
|
||||
# TODO: fix variable extraction
|
||||
# content = "abc$SECRET_KEYdef"
|
||||
@@ -71,12 +56,9 @@ class TestContext(ApiServerUnittest):
|
||||
"random": "${gen_random_string(5)}",
|
||||
"data": '{"name": "user", "password": "123456"}',
|
||||
"authorization": "${gen_md5($TOKEN, $data, $random)}",
|
||||
"TOKEN": "debugtalk"
|
||||
}
|
||||
functions = {
|
||||
"gen_random_string": gen_random_string,
|
||||
"gen_md5": gen_md5
|
||||
"TOKEN": "debugtalk",
|
||||
}
|
||||
functions = {"gen_random_string": gen_random_string, "gen_md5": gen_md5}
|
||||
variables = parser.prepare_lazy_data(variables, functions, variables.keys())
|
||||
variables = parser.parse_variables_mapping(variables)
|
||||
self.context.init_test_variables(variables)
|
||||
@@ -88,14 +70,12 @@ class TestContext(ApiServerUnittest):
|
||||
"Content-Type": "application/json",
|
||||
"authorization": "$authorization",
|
||||
"random": "$random",
|
||||
"secret_key": "$SECRET_KEY"
|
||||
"secret_key": "$SECRET_KEY",
|
||||
},
|
||||
"data": "$data"
|
||||
"data": "$data",
|
||||
}
|
||||
prepared_request = parser.prepare_lazy_data(
|
||||
request,
|
||||
functions,
|
||||
{"authorization", "random", "SECRET_KEY", "data"}
|
||||
request, functions, {"authorization", "random", "SECRET_KEY", "data"}
|
||||
)
|
||||
parsed_request = self.context.eval_content(prepared_request)
|
||||
self.assertIn("authorization", parsed_request["headers"])
|
||||
@@ -104,17 +84,14 @@ class TestContext(ApiServerUnittest):
|
||||
self.assertEqual(len(parsed_request["headers"]["random"]), 5)
|
||||
self.assertIn("data", parsed_request)
|
||||
self.assertEqual(
|
||||
parsed_request["data"],
|
||||
'{"name": "user", "password": "123456"}'
|
||||
parsed_request["data"], '{"name": "user", "password": "123456"}'
|
||||
)
|
||||
self.assertEqual(parsed_request["headers"]["secret_key"], "DebugTalk")
|
||||
|
||||
def test_validate(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
'name': "test validation"
|
||||
},
|
||||
"config": {"name": "test validation"},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "test validation",
|
||||
@@ -124,26 +101,32 @@ class TestContext(ApiServerUnittest):
|
||||
},
|
||||
"variables": {
|
||||
"resp_status_code": 200,
|
||||
"resp_body_success": True
|
||||
"resp_body_success": True,
|
||||
},
|
||||
"validate": [
|
||||
{"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": "${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
|
||||
|
||||
tests_mapping = {
|
||||
"project_mapping": {
|
||||
"functions": {
|
||||
"is_status_code_200": is_status_code_200
|
||||
}
|
||||
"functions": {"is_status_code_200": is_status_code_200}
|
||||
},
|
||||
"testcases": testcases
|
||||
"testcases": testcases,
|
||||
}
|
||||
testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = testcases[0]
|
||||
@@ -154,9 +137,7 @@ class TestContext(ApiServerUnittest):
|
||||
def test_validate_exception(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
'name': "test validation"
|
||||
},
|
||||
"config": {"name": "test validation"},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "test validation",
|
||||
@@ -166,20 +147,22 @@ class TestContext(ApiServerUnittest):
|
||||
},
|
||||
"variables": {
|
||||
"resp_status_code": 200,
|
||||
"resp_body_success": True
|
||||
"resp_body_success": True,
|
||||
},
|
||||
"validate": [
|
||||
{"eq": ["$resp_status_code", 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 = {
|
||||
"testcases": testcases
|
||||
}
|
||||
tests_mapping = {"testcases": testcases}
|
||||
testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = testcases[0]
|
||||
test_runner = runner.Runner(parsed_testcase["config"])
|
||||
|
||||
@@ -5,15 +5,13 @@ from httprunner.ext.locusts.utils import prepare_locust_tests
|
||||
|
||||
|
||||
class TestLocust(unittest.TestCase):
|
||||
|
||||
def test_prepare_locust_tests(self):
|
||||
path = os.path.join(
|
||||
os.getcwd(), 'tests/locust_tests/demo_locusts.yml')
|
||||
path = os.path.join(os.getcwd(), "tests/locust_tests/demo_locusts.yml")
|
||||
locust_tests = prepare_locust_tests(path)
|
||||
self.assertEqual(len(locust_tests), 2 + 3)
|
||||
name_list = [
|
||||
"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[4]["config"]["name"], name_list)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
@@ -7,14 +6,15 @@ from httprunner.loader import buildup
|
||||
|
||||
|
||||
class TestModuleLoader(unittest.TestCase):
|
||||
|
||||
def test_filter_module_functions(self):
|
||||
module_functions = buildup.load_module_functions(buildup)
|
||||
self.assertIn("load_module_functions", module_functions)
|
||||
self.assertNotIn("is_py3", module_functions)
|
||||
|
||||
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"])
|
||||
|
||||
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_working_directory = project_mapping["PWD"]
|
||||
debugtalk_functions = project_mapping["functions"]
|
||||
self.assertEqual(
|
||||
project_working_directory,
|
||||
os.path.join(os.getcwd(), "tests")
|
||||
)
|
||||
self.assertEqual(project_working_directory, os.path.join(os.getcwd(), "tests"))
|
||||
self.assertIn("gen_md5", debugtalk_functions)
|
||||
|
||||
project_mapping = buildup.load_project_data("tests/base.py")
|
||||
project_working_directory = project_mapping["PWD"]
|
||||
debugtalk_functions = project_mapping["functions"]
|
||||
self.assertEqual(
|
||||
project_working_directory,
|
||||
os.path.join(os.getcwd(), "tests")
|
||||
)
|
||||
self.assertEqual(project_working_directory, os.path.join(os.getcwd(), "tests"))
|
||||
self.assertIn("gen_md5", debugtalk_functions)
|
||||
|
||||
project_mapping = buildup.load_project_data("httprunner/__init__.py")
|
||||
project_working_directory = project_mapping["PWD"]
|
||||
debugtalk_functions = project_mapping["functions"]
|
||||
self.assertEqual(
|
||||
project_working_directory,
|
||||
os.getcwd()
|
||||
)
|
||||
self.assertEqual(project_working_directory, os.getcwd())
|
||||
self.assertEqual(debugtalk_functions, {})
|
||||
|
||||
|
||||
class TestSuiteLoader(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
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
|
||||
|
||||
def test_load_teststep_api(self):
|
||||
raw_test = {
|
||||
"name": "create user (override).",
|
||||
"api": "api/create_user.yml",
|
||||
"variables": [
|
||||
{"uid": "999"}
|
||||
]
|
||||
"variables": [{"uid": "999"}],
|
||||
}
|
||||
teststep = buildup.load_teststep(raw_test)
|
||||
self.assertEqual(
|
||||
"create user (override).",
|
||||
teststep["name"]
|
||||
)
|
||||
self.assertEqual("create user (override).", teststep["name"])
|
||||
self.assertIn("api_def", teststep)
|
||||
api_def = teststep["api_def"]
|
||||
self.assertEqual(api_def["name"], "create user")
|
||||
@@ -82,15 +69,10 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
raw_test = {
|
||||
"name": "setup and reset all (override).",
|
||||
"testcase": "testcases/setup.yml",
|
||||
"variables": [
|
||||
{"device_sn": "$device_sn"}
|
||||
]
|
||||
"variables": [{"device_sn": "$device_sn"}],
|
||||
}
|
||||
testcase = buildup.load_teststep(raw_test)
|
||||
self.assertEqual(
|
||||
"setup and reset all (override).",
|
||||
testcase["name"]
|
||||
)
|
||||
self.assertEqual("setup and reset all (override).", testcase["name"])
|
||||
tests = testcase["testcase_def"]["teststeps"]
|
||||
self.assertEqual(len(tests), 2)
|
||||
self.assertEqual(tests[0]["name"], "get token (setup)")
|
||||
@@ -106,7 +88,7 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
def test_load_test_file_testcase(self):
|
||||
for loaded_content in [
|
||||
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.assertIn("path", loaded_content)
|
||||
@@ -118,7 +100,7 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
def test_load_test_file_testcase_v2(self):
|
||||
for loaded_content in [
|
||||
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.assertIn("path", loaded_content)
|
||||
@@ -130,38 +112,45 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
def test_load_test_file_testsuite(self):
|
||||
for loaded_content in [
|
||||
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")
|
||||
|
||||
testcases = loaded_content["testcases"]
|
||||
self.assertEqual(len(testcases), 2)
|
||||
self.assertIn('create user 1000 and check result.', testcases)
|
||||
self.assertIn('testcase_def', testcases["create user 1000 and check result."])
|
||||
self.assertIn("create user 1000 and check result.", testcases)
|
||||
self.assertIn(
|
||||
"testcase_def", testcases["create user 1000 and check result."]
|
||||
)
|
||||
self.assertEqual(
|
||||
testcases["create user 1000 and check result."]["testcase_def"]["config"]["name"],
|
||||
"create user and check result."
|
||||
testcases["create user 1000 and check result."]["testcase_def"][
|
||||
"config"
|
||||
]["name"],
|
||||
"create user and check result.",
|
||||
)
|
||||
|
||||
def test_load_test_file_testsuite_v2(self):
|
||||
for loaded_content in [
|
||||
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")
|
||||
|
||||
testcases = loaded_content["testcases"]
|
||||
self.assertEqual(len(testcases), 2)
|
||||
self.assertIn('create user 1000 and check result.', testcases)
|
||||
self.assertIn('testcase_def', testcases["create user 1000 and check result."])
|
||||
self.assertIn("create user 1000 and check result.", testcases)
|
||||
self.assertIn(
|
||||
"testcase_def", testcases["create user 1000 and check result."]
|
||||
)
|
||||
self.assertEqual(
|
||||
testcases["create user 1000 and check result."]["testcase_def"]["config"]["name"],
|
||||
"create user and check result."
|
||||
testcases["create user 1000 and check result."]["testcase_def"][
|
||||
"config"
|
||||
]["name"],
|
||||
"create user and check result.",
|
||||
)
|
||||
|
||||
def test_load_tests_api_file(self):
|
||||
path = os.path.join(
|
||||
os.getcwd(), 'tests/api/create_user.yml')
|
||||
path = os.path.join(os.getcwd(), "tests/api/create_user.yml")
|
||||
tests_mapping = loader.load_cases(path)
|
||||
project_mapping = tests_mapping["project_mapping"]
|
||||
api_list = tests_mapping["apis"]
|
||||
@@ -170,8 +159,7 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
|
||||
def test_load_tests_testcase_file(self):
|
||||
# absolute file path
|
||||
path = os.path.join(
|
||||
os.getcwd(), 'tests/data/demo_testcase_hardcode.json')
|
||||
path = os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.json")
|
||||
tests_mapping = loader.load_cases(path)
|
||||
project_mapping = tests_mapping["project_mapping"]
|
||||
testcases_list = tests_mapping["testcases"]
|
||||
@@ -180,7 +168,7 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
self.assertIn("get_sign", project_mapping["functions"])
|
||||
|
||||
# relative file path
|
||||
path = 'tests/data/demo_testcase_hardcode.yml'
|
||||
path = "tests/data/demo_testcase_hardcode.yml"
|
||||
tests_mapping = loader.load_cases(path)
|
||||
project_mapping = tests_mapping["project_mapping"]
|
||||
testcases_list = tests_mapping["testcases"]
|
||||
@@ -189,91 +177,75 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
self.assertIn("get_sign", project_mapping["functions"])
|
||||
|
||||
def test_load_tests_testcase_file_2(self):
|
||||
testcase_file_path = os.path.join(
|
||||
os.getcwd(), 'tests/data/demo_testcase.yml')
|
||||
testcase_file_path = os.path.join(os.getcwd(), "tests/data/demo_testcase.yml")
|
||||
tests_mapping = loader.load_cases(testcase_file_path)
|
||||
testcases = tests_mapping["testcases"]
|
||||
self.assertIsInstance(testcases, list)
|
||||
self.assertEqual(testcases[0]["config"]["name"], '123t$var_a')
|
||||
self.assertIn(
|
||||
"sum_two",
|
||||
tests_mapping["project_mapping"]["functions"]
|
||||
self.assertEqual(testcases[0]["config"]["name"], "123t$var_a")
|
||||
self.assertIn("sum_two", tests_mapping["project_mapping"]["functions"])
|
||||
self.assertEqual(
|
||||
testcases[0]["config"]["variables"]["var_c"], "${sum_two($var_a, $var_b)}"
|
||||
)
|
||||
self.assertEqual(
|
||||
testcases[0]["config"]["variables"]["var_c"],
|
||||
"${sum_two($var_a, $var_b)}"
|
||||
)
|
||||
self.assertEqual(
|
||||
testcases[0]["config"]["variables"]["PROJECT_KEY"],
|
||||
"${ENV(PROJECT_KEY)}"
|
||||
testcases[0]["config"]["variables"]["PROJECT_KEY"], "${ENV(PROJECT_KEY)}"
|
||||
)
|
||||
|
||||
def test_load_tests_testcase_file_with_api_ref(self):
|
||||
path = os.path.join(
|
||||
os.getcwd(), 'tests/data/demo_testcase_layer.yml')
|
||||
path = os.path.join(os.getcwd(), "tests/data/demo_testcase_layer.yml")
|
||||
tests_mapping = loader.load_cases(path)
|
||||
project_mapping = tests_mapping["project_mapping"]
|
||||
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("base_url", testcases_list[0]["config"])
|
||||
test_dict0 = testcases_list[0]["teststeps"][0]
|
||||
self.assertEqual(
|
||||
"get token with $user_agent, $app_version",
|
||||
test_dict0["name"]
|
||||
)
|
||||
self.assertEqual("get token with $user_agent, $app_version", test_dict0["name"])
|
||||
self.assertIn("/api/get-token", test_dict0["api_def"]["request"]["url"])
|
||||
self.assertIn(
|
||||
{'eq': ['status_code', 200]},
|
||||
test_dict0["validate"]
|
||||
)
|
||||
self.assertIn({"eq": ["status_code", 200]}, test_dict0["validate"])
|
||||
|
||||
def test_load_tests_testsuite_file_with_testcase_ref(self):
|
||||
path = os.path.join(
|
||||
os.getcwd(), 'tests/testsuites/create_users.yml')
|
||||
path = os.path.join(os.getcwd(), "tests/testsuites/create_users.yml")
|
||||
tests_mapping = loader.load_cases(path)
|
||||
project_mapping = tests_mapping["project_mapping"]
|
||||
testsuites_list = tests_mapping["testsuites"]
|
||||
|
||||
self.assertEqual("create users with uid", testsuites_list[0]["config"]["name"])
|
||||
self.assertEqual(
|
||||
"create users with uid",
|
||||
testsuites_list[0]["config"]["name"]
|
||||
)
|
||||
self.assertEqual(
|
||||
'${gen_random_string(15)}',
|
||||
testsuites_list[0]["config"]["variables"]['device_sn']
|
||||
"${gen_random_string(15)}",
|
||||
testsuites_list[0]["config"]["variables"]["device_sn"],
|
||||
)
|
||||
self.assertIn(
|
||||
"create user 1000 and check result.",
|
||||
testsuites_list[0]["testcases"]
|
||||
"create user 1000 and check result.", testsuites_list[0]["testcases"]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
testsuites_list[0]["testcases"]["create user 1000 and check result."]["testcase_def"]["config"]["name"],
|
||||
"create user and check result."
|
||||
testsuites_list[0]["testcases"]["create user 1000 and check result."][
|
||||
"testcase_def"
|
||||
]["config"]["name"],
|
||||
"create user and check result.",
|
||||
)
|
||||
|
||||
def test_load_tests_folder_path(self):
|
||||
# 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)
|
||||
testcase_list_1 = tests_mapping["testcases"]
|
||||
self.assertGreater(len(testcase_list_1), 4)
|
||||
|
||||
# relative folder path
|
||||
path = 'tests/data/'
|
||||
path = "tests/data/"
|
||||
tests_mapping = loader.load_cases(path)
|
||||
testcase_list_2 = tests_mapping["testcases"]
|
||||
self.assertEqual(len(testcase_list_1), len(testcase_list_2))
|
||||
|
||||
def test_load_tests_path_not_exist(self):
|
||||
# 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):
|
||||
loader.load_cases(path)
|
||||
|
||||
# relative folder path
|
||||
path = 'tests/data_not_exist'
|
||||
path = "tests/data_not_exist"
|
||||
with self.assertRaises(exceptions.FileNotFound):
|
||||
loader.load_cases(path)
|
||||
|
||||
@@ -281,5 +253,11 @@ class TestSuiteLoader(unittest.TestCase):
|
||||
buildup.load_project_data(os.path.join(os.getcwd(), "tests"))
|
||||
self.assertIn("gen_md5", self.project_mapping["functions"])
|
||||
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.project_mapping["test_path"], os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
|
||||
self.assertEqual(
|
||||
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):
|
||||
|
||||
def test_is_testcases(self):
|
||||
data_structure = "path/to/file"
|
||||
self.assertFalse(check.is_test_content(data_structure))
|
||||
@@ -12,11 +11,7 @@ class TestLoaderCheck(unittest.TestCase):
|
||||
self.assertFalse(check.is_test_content(data_structure))
|
||||
|
||||
data_structure = {
|
||||
"project_mapping": {
|
||||
"PWD": "XXXXX",
|
||||
"functions": {},
|
||||
"env": {}
|
||||
},
|
||||
"project_mapping": {"PWD": "XXXXX", "functions": {}, "env": {}},
|
||||
"testcases": [
|
||||
{ # testcase data structure
|
||||
"config": {
|
||||
@@ -27,19 +22,19 @@ class TestLoaderCheck(unittest.TestCase):
|
||||
"teststeps": [
|
||||
# test data structure
|
||||
{
|
||||
'name': 'test step desc1',
|
||||
'variables': [], # optional
|
||||
'extract': {}, # optional
|
||||
'validate': [],
|
||||
'request': {
|
||||
"name": "test step desc1",
|
||||
"variables": [], # optional
|
||||
"extract": {}, # optional
|
||||
"validate": [],
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://docs.httprunner.org"
|
||||
}
|
||||
"url": "https://docs.httprunner.org",
|
||||
},
|
||||
},
|
||||
# test_dict2 # another test dict
|
||||
]
|
||||
],
|
||||
},
|
||||
# testcase_dict_2 # another testcase dict
|
||||
]
|
||||
],
|
||||
}
|
||||
self.assertTrue(check.is_test_content(data_structure))
|
||||
|
||||
@@ -7,11 +7,10 @@ from httprunner.loader.buildup import load_test_file
|
||||
|
||||
|
||||
class TestFileLoader(unittest.TestCase):
|
||||
|
||||
def test_load_yaml_file_file_format_error(self):
|
||||
yaml_tmp_file = "tests/data/tmp.yml"
|
||||
# create empty yaml file
|
||||
with open(yaml_tmp_file, 'w') as f:
|
||||
with open(yaml_tmp_file, "w") as f:
|
||||
f.write("")
|
||||
|
||||
with self.assertRaises(exceptions.FileFormatError):
|
||||
@@ -20,7 +19,7 @@ class TestFileLoader(unittest.TestCase):
|
||||
os.remove(yaml_tmp_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")
|
||||
|
||||
with self.assertRaises(exceptions.FileFormatError):
|
||||
@@ -31,7 +30,7 @@ class TestFileLoader(unittest.TestCase):
|
||||
def test_load_json_file_file_format_error(self):
|
||||
json_tmp_file = "tests/data/tmp.json"
|
||||
# create empty file
|
||||
with open(json_tmp_file, 'w') as f:
|
||||
with open(json_tmp_file, "w") as f:
|
||||
f.write("")
|
||||
|
||||
with self.assertRaises(exceptions.FileFormatError):
|
||||
@@ -40,7 +39,7 @@ class TestFileLoader(unittest.TestCase):
|
||||
os.remove(json_tmp_file)
|
||||
|
||||
# create empty json file
|
||||
with open(json_tmp_file, 'w') as f:
|
||||
with open(json_tmp_file, "w") as f:
|
||||
f.write("{}")
|
||||
|
||||
with self.assertRaises(exceptions.FileFormatError):
|
||||
@@ -49,7 +48,7 @@ class TestFileLoader(unittest.TestCase):
|
||||
os.remove(json_tmp_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")
|
||||
|
||||
with self.assertRaises(exceptions.FileFormatError):
|
||||
@@ -58,62 +57,62 @@ class TestFileLoader(unittest.TestCase):
|
||||
os.remove(json_tmp_file)
|
||||
|
||||
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):
|
||||
load.load_file(testcase_file_path)
|
||||
|
||||
def test_load_json_testcases(self):
|
||||
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)
|
||||
self.assertEqual(len(testcases), 3)
|
||||
test = testcases[0]["test"]
|
||||
self.assertIn('name', test)
|
||||
self.assertIn('request', test)
|
||||
self.assertIn('url', test['request'])
|
||||
self.assertIn('method', test['request'])
|
||||
self.assertIn("name", test)
|
||||
self.assertIn("request", test)
|
||||
self.assertIn("url", test["request"])
|
||||
self.assertIn("method", test["request"])
|
||||
|
||||
def test_load_yaml_testcases(self):
|
||||
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)
|
||||
self.assertEqual(len(testcases), 3)
|
||||
test = testcases[0]["test"]
|
||||
self.assertIn('name', test)
|
||||
self.assertIn('request', test)
|
||||
self.assertIn('url', test['request'])
|
||||
self.assertIn('method', test['request'])
|
||||
self.assertIn("name", test)
|
||||
self.assertIn("request", test)
|
||||
self.assertIn("url", test["request"])
|
||||
self.assertIn("method", test["request"])
|
||||
|
||||
def test_load_csv_file_one_parameter(self):
|
||||
csv_file_path = os.path.join(
|
||||
os.getcwd(), 'tests/data/user_agent.csv')
|
||||
csv_file_path = os.path.join(os.getcwd(), "tests/data/user_agent.csv")
|
||||
csv_content = load.load_file(csv_file_path)
|
||||
self.assertEqual(
|
||||
csv_content,
|
||||
[
|
||||
{'user_agent': 'iOS/10.1'},
|
||||
{'user_agent': 'iOS/10.2'},
|
||||
{'user_agent': 'iOS/10.3'}
|
||||
]
|
||||
{"user_agent": "iOS/10.1"},
|
||||
{"user_agent": "iOS/10.2"},
|
||||
{"user_agent": "iOS/10.3"},
|
||||
],
|
||||
)
|
||||
|
||||
def test_load_csv_file_multiple_parameters(self):
|
||||
csv_file_path = os.path.join(
|
||||
os.getcwd(), 'tests/data/account.csv')
|
||||
csv_file_path = os.path.join(os.getcwd(), "tests/data/account.csv")
|
||||
csv_content = load.load_file(csv_file_path)
|
||||
self.assertEqual(
|
||||
csv_content,
|
||||
[
|
||||
{'username': 'test1', 'password': '111111'},
|
||||
{'username': 'test2', 'password': '222222'},
|
||||
{'username': 'test3', 'password': '333333'}
|
||||
]
|
||||
{"username": "test1", "password": "111111"},
|
||||
{"username": "test2", "password": "222222"},
|
||||
{"username": "test3", "password": "333333"},
|
||||
],
|
||||
)
|
||||
|
||||
def test_load_folder_files(self):
|
||||
folder = os.path.join(os.getcwd(), 'tests')
|
||||
file1 = os.path.join(os.getcwd(), 'tests', 'test_utils.py')
|
||||
file2 = os.path.join(os.getcwd(), 'tests', 'api', 'reset_all.yml')
|
||||
folder = os.path.join(os.getcwd(), "tests")
|
||||
file1 = os.path.join(os.getcwd(), "tests", "test_utils.py")
|
||||
file2 = os.path.join(os.getcwd(), "tests", "api", "reset_all.yml")
|
||||
|
||||
files = load.load_folder_files(folder, recursive=False)
|
||||
self.assertEqual(files, [])
|
||||
@@ -129,25 +128,25 @@ class TestFileLoader(unittest.TestCase):
|
||||
self.assertEqual([], files)
|
||||
|
||||
def test_load_dot_env_file(self):
|
||||
dot_env_path = os.path.join(
|
||||
os.getcwd(), "tests", ".env"
|
||||
)
|
||||
dot_env_path = os.path.join(os.getcwd(), "tests", ".env")
|
||||
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
||||
self.assertIn("PROJECT_KEY", env_variables_mapping)
|
||||
self.assertEqual(env_variables_mapping["UserName"], "debugtalk")
|
||||
|
||||
def test_load_custom_dot_env_file(self):
|
||||
dot_env_path = os.path.join(
|
||||
os.getcwd(), "tests", "data", "test.env"
|
||||
)
|
||||
dot_env_path = os.path.join(os.getcwd(), "tests", "data", "test.env")
|
||||
env_variables_mapping = load.load_dot_env_file(dot_env_path)
|
||||
self.assertIn("PROJECT_KEY", env_variables_mapping)
|
||||
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):
|
||||
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)
|
||||
self.assertEqual(env_variables_mapping, {})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
@@ -7,7 +6,6 @@ from httprunner.loader import locate
|
||||
|
||||
|
||||
class TestLoaderLocate(unittest.TestCase):
|
||||
|
||||
def test_locate_file(self):
|
||||
with self.assertRaises(exceptions.FileNotFound):
|
||||
locate.locate_file(os.getcwd(), "debugtalk.py")
|
||||
@@ -18,23 +16,21 @@ class TestLoaderLocate(unittest.TestCase):
|
||||
start_path = os.path.join(os.getcwd(), "tests")
|
||||
self.assertEqual(
|
||||
locate.locate_file(start_path, "debugtalk.py"),
|
||||
os.path.join(
|
||||
os.getcwd(), "tests/debugtalk.py"
|
||||
)
|
||||
os.path.join(os.getcwd(), "tests/debugtalk.py"),
|
||||
)
|
||||
self.assertEqual(
|
||||
locate.locate_file("tests/", "debugtalk.py"),
|
||||
os.path.join(os.getcwd(), "tests", "debugtalk.py")
|
||||
os.path.join(os.getcwd(), "tests", "debugtalk.py"),
|
||||
)
|
||||
self.assertEqual(
|
||||
locate.locate_file("tests", "debugtalk.py"),
|
||||
os.path.join(os.getcwd(), "tests", "debugtalk.py")
|
||||
os.path.join(os.getcwd(), "tests", "debugtalk.py"),
|
||||
)
|
||||
self.assertEqual(
|
||||
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(
|
||||
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):
|
||||
|
||||
def test_parse_response_object_json(self):
|
||||
url = "http://127.0.0.1:5000/api/users"
|
||||
resp = requests.get(url)
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
self.assertTrue(hasattr(resp_obj, 'status_code'))
|
||||
self.assertTrue(hasattr(resp_obj, 'headers'))
|
||||
self.assertTrue(hasattr(resp_obj, 'content'))
|
||||
self.assertIn('Content-Type', resp_obj.headers)
|
||||
self.assertIn('Content-Length', resp_obj.headers)
|
||||
self.assertIn('success', resp_obj.json)
|
||||
self.assertTrue(hasattr(resp_obj, "status_code"))
|
||||
self.assertTrue(hasattr(resp_obj, "headers"))
|
||||
self.assertTrue(hasattr(resp_obj, "content"))
|
||||
self.assertIn("Content-Type", resp_obj.headers)
|
||||
self.assertIn("Content-Length", resp_obj.headers)
|
||||
self.assertIn("success", resp_obj.json)
|
||||
|
||||
def test_parse_response_object_content(self):
|
||||
url = "http://127.0.0.1:5000/"
|
||||
@@ -29,19 +28,12 @@ class TestResponse(ApiServerUnittest):
|
||||
resp = requests.get(url="{}/status/200".format(HTTPBIN_SERVER))
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_status_code": "status_code"}
|
||||
]
|
||||
extract_binds_list = [{"resp_status_code": "status_code"}]
|
||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_status_code"],
|
||||
200
|
||||
)
|
||||
self.assertEqual(extract_binds_dict["resp_status_code"], 200)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_status_code": "status_code.xx"}
|
||||
]
|
||||
extract_binds_list = [{"resp_status_code": "status_code.xx"}]
|
||||
with self.assertRaises(exceptions.ParamsError):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
@@ -53,14 +45,16 @@ class TestResponse(ApiServerUnittest):
|
||||
{"resp_encoding": "encoding"},
|
||||
{"resp_ok": "ok"},
|
||||
{"resp_reason": "reason"},
|
||||
{"resp_url": "url"}
|
||||
{"resp_url": "url"},
|
||||
]
|
||||
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_ok"], True)
|
||||
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"}]
|
||||
with self.assertRaises(exceptions.ParamsError):
|
||||
@@ -81,24 +75,15 @@ class TestResponse(ApiServerUnittest):
|
||||
def test_extract_response_cookies(self):
|
||||
resp = requests.get(
|
||||
url="{}/cookies".format(HTTPBIN_SERVER),
|
||||
headers={
|
||||
"accept": "application/json"
|
||||
}
|
||||
headers={"accept": "application/json"},
|
||||
)
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_cookies": "cookies"}
|
||||
]
|
||||
extract_binds_list = [{"resp_cookies": "cookies"}]
|
||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_cookies"],
|
||||
{}
|
||||
)
|
||||
self.assertEqual(extract_binds_dict["resp_cookies"], {})
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_cookies": "cookies.xx"}
|
||||
]
|
||||
extract_binds_list = [{"resp_cookies": "cookies.xx"}]
|
||||
with self.assertRaises(exceptions.ExtractFailure):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
@@ -106,22 +91,20 @@ class TestResponse(ApiServerUnittest):
|
||||
resp = requests.post(
|
||||
url="{}/anything".format(HTTPBIN_SERVER),
|
||||
json={
|
||||
'success': False,
|
||||
"success": False,
|
||||
"person": {
|
||||
"name": {
|
||||
"first_name": "Leo",
|
||||
"last_name": "Lee",
|
||||
},
|
||||
"age": 29,
|
||||
"cities": ["Guangzhou", "Shenzhen"]
|
||||
}
|
||||
}
|
||||
"cities": ["Guangzhou", "Shenzhen"],
|
||||
},
|
||||
},
|
||||
)
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_elapsed": "elapsed"}
|
||||
]
|
||||
extract_binds_list = [{"resp_elapsed": "elapsed"}]
|
||||
with self.assertRaises(exceptions.ParamsError):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
@@ -129,7 +112,7 @@ class TestResponse(ApiServerUnittest):
|
||||
{"resp_elapsed_microseconds": "elapsed.microseconds"},
|
||||
{"resp_elapsed_seconds": "elapsed.seconds"},
|
||||
{"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)
|
||||
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.assertGreater(extract_binds_dict["resp_elapsed_total_seconds"], 0)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_elapsed": "elapsed.years"}
|
||||
]
|
||||
extract_binds_list = [{"resp_elapsed": "elapsed.years"}]
|
||||
with self.assertRaises(exceptions.ParamsError):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
@@ -150,16 +131,16 @@ class TestResponse(ApiServerUnittest):
|
||||
extract_binds_list = [
|
||||
{"resp_headers": "headers"},
|
||||
{"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)
|
||||
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_lowercase"])
|
||||
self.assertIn(
|
||||
"text/html", extract_binds_dict["resp_headers_content_type_lowercase"]
|
||||
)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_headers_xxx": "headers.xxx"}
|
||||
]
|
||||
extract_binds_list = [{"resp_headers_xxx": "headers.xxx"}]
|
||||
with self.assertRaises(exceptions.ExtractFailure):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
@@ -167,16 +148,16 @@ class TestResponse(ApiServerUnittest):
|
||||
resp = requests.post(
|
||||
url="{}/anything".format(HTTPBIN_SERVER),
|
||||
json={
|
||||
'success': False,
|
||||
"success": False,
|
||||
"person": {
|
||||
"name": {
|
||||
"first_name": "Leo",
|
||||
"last_name": "Lee",
|
||||
},
|
||||
"age": 29,
|
||||
"cities": ["Guangzhou", "Shenzhen"]
|
||||
}
|
||||
}
|
||||
"cities": ["Guangzhou", "Shenzhen"],
|
||||
},
|
||||
},
|
||||
)
|
||||
# resp.json()
|
||||
# {
|
||||
@@ -218,51 +199,31 @@ class TestResponse(ApiServerUnittest):
|
||||
{"resp_content_content_success": "content.json.success"},
|
||||
{"resp_content_text_success": "text.json.success"},
|
||||
{"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)
|
||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_headers_content_type"],
|
||||
"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"
|
||||
extract_binds_dict["resp_headers_content_type"], "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")
|
||||
|
||||
def test_extract_response_body_html(self):
|
||||
resp = requests.get(url=HTTPBIN_SERVER)
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_content": "content"}
|
||||
]
|
||||
extract_binds_list = [{"resp_content": "content"}]
|
||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
self.assertIsInstance(extract_binds_dict["resp_content"], basestring)
|
||||
self.assertIn("httpbin.org", extract_binds_dict["resp_content"])
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_content": "content.xxx"}
|
||||
]
|
||||
extract_binds_list = [{"resp_content": "content.xxx"}]
|
||||
with self.assertRaises(exceptions.ExtractFailure):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
@@ -272,7 +233,7 @@ class TestResponse(ApiServerUnittest):
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_others_encoding": "encoding"},
|
||||
{"resp_others_history": "history"}
|
||||
{"resp_others_history": "history"},
|
||||
]
|
||||
with self.assertRaises(exceptions.ParamsError):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
@@ -281,21 +242,19 @@ class TestResponse(ApiServerUnittest):
|
||||
resp = requests.post(
|
||||
url="{}/anything".format(HTTPBIN_SERVER),
|
||||
json={
|
||||
'success': False,
|
||||
"success": False,
|
||||
"person": {
|
||||
"name": {
|
||||
"first_name": "Leo",
|
||||
"last_name": "Lee",
|
||||
},
|
||||
"age": 29,
|
||||
"cities": ["Guangzhou", "Shenzhen"]
|
||||
}
|
||||
}
|
||||
"cities": ["Guangzhou", "Shenzhen"],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_content_dict_key_error": "content.not_exist"}
|
||||
]
|
||||
extract_binds_list = [{"resp_content_dict_key_error": "content.not_exist"}]
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
with self.assertRaises(exceptions.ExtractFailure):
|
||||
@@ -310,80 +269,49 @@ class TestResponse(ApiServerUnittest):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
def test_extract_response_json_string(self):
|
||||
resp = requests.post(
|
||||
url="{}/anything".format(HTTPBIN_SERVER),
|
||||
data="abc"
|
||||
)
|
||||
resp = requests.post(url="{}/anything".format(HTTPBIN_SERVER), data="abc")
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_content_body": "content.data"}
|
||||
]
|
||||
extract_binds_list = [{"resp_content_body": "content.data"}]
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_content_body"],
|
||||
"abc"
|
||||
)
|
||||
self.assertEqual(extract_binds_dict["resp_content_body"], "abc")
|
||||
|
||||
def test_extract_text_response(self):
|
||||
resp = requests.post(
|
||||
url="{}/anything".format(HTTPBIN_SERVER),
|
||||
data="LB123abcRB789"
|
||||
url="{}/anything".format(HTTPBIN_SERVER), data="LB123abcRB789"
|
||||
)
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_content_key1": "LB123(.*)RB789"},
|
||||
{"resp_content_key2": "LB[\d]*(.*)RB[\d]*"},
|
||||
{"resp_content_key3": "LB[\d]*(.*)9"}
|
||||
{"resp_content_key3": "LB[\d]*(.*)9"},
|
||||
]
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
|
||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_content_key1"],
|
||||
"abc"
|
||||
)
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_content_key2"],
|
||||
"abc"
|
||||
)
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_content_key3"],
|
||||
"abcRB78"
|
||||
)
|
||||
self.assertEqual(extract_binds_dict["resp_content_key1"], "abc")
|
||||
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):
|
||||
resp = requests.post(
|
||||
url="{}/anything".format(HTTPBIN_SERVER),
|
||||
data="LB123abcRB789"
|
||||
url="{}/anything".format(HTTPBIN_SERVER), data="LB123abcRB789"
|
||||
)
|
||||
extract_binds_list = [
|
||||
{"resp_content_key1": "LB123.*RB789"}
|
||||
]
|
||||
extract_binds_list = [{"resp_content_key1": "LB123.*RB789"}]
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
with self.assertRaises(exceptions.ParamsError):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
def test_extract_response_empty(self):
|
||||
resp = requests.post(
|
||||
url="{}/anything".format(HTTPBIN_SERVER),
|
||||
data="abc"
|
||||
)
|
||||
resp = requests.post(url="{}/anything".format(HTTPBIN_SERVER), data="abc")
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_content_body": "content.data"}
|
||||
]
|
||||
extract_binds_list = [{"resp_content_body": "content.data"}]
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
extract_binds_dict = resp_obj.extract_response(extract_binds_list)
|
||||
self.assertEqual(
|
||||
extract_binds_dict["resp_content_body"],
|
||||
'abc'
|
||||
)
|
||||
self.assertEqual(extract_binds_dict["resp_content_body"], "abc")
|
||||
|
||||
extract_binds_list = [
|
||||
{"resp_content_body": "content.data.def"}
|
||||
]
|
||||
extract_binds_list = [{"resp_content_body": "content.data.def"}]
|
||||
resp_obj = response.ResponseObject(resp)
|
||||
with self.assertRaises(exceptions.ExtractFailure):
|
||||
resp_obj.extract_response(extract_binds_list)
|
||||
|
||||
@@ -7,16 +7,11 @@ from tests.base import ApiServerUnittest
|
||||
|
||||
|
||||
class TestRunner(ApiServerUnittest):
|
||||
|
||||
def setUp(self):
|
||||
project_mapping = loader.load_project_data(os.path.join(os.getcwd(), "tests"))
|
||||
self.debugtalk_functions = project_mapping["functions"]
|
||||
|
||||
config = {
|
||||
"name": "XXX",
|
||||
"base_url": "http://127.0.0.1",
|
||||
"verify": False
|
||||
}
|
||||
config = {"name": "XXX", "base_url": "http://127.0.0.1", "verify": False}
|
||||
self.test_runner = runner.Runner(config)
|
||||
self.reset_all()
|
||||
|
||||
@@ -27,10 +22,8 @@ class TestRunner(ApiServerUnittest):
|
||||
|
||||
def test_run_single_testcase(self):
|
||||
testcase_file_path_list = [
|
||||
os.path.join(
|
||||
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.yml"),
|
||||
os.path.join(os.getcwd(), "tests/data/demo_testcase_hardcode.json"),
|
||||
]
|
||||
|
||||
for testcase_file_path in testcase_file_path_list:
|
||||
@@ -50,14 +43,8 @@ class TestRunner(ApiServerUnittest):
|
||||
"config": {
|
||||
"name": "basic test with httpbin",
|
||||
"base_url": HTTPBIN_SERVER,
|
||||
"setup_hooks": [
|
||||
"${sleep(0.5)}",
|
||||
"${hook_print(setup)}"
|
||||
],
|
||||
"teardown_hooks": [
|
||||
"${sleep(1)}",
|
||||
"${hook_print(teardown)}"
|
||||
]
|
||||
"setup_hooks": ["${sleep(0.5)}", "${hook_print(setup)}"],
|
||||
"teardown_hooks": ["${sleep(1)}", "${hook_print(teardown)}"],
|
||||
},
|
||||
"teststeps": [
|
||||
{
|
||||
@@ -70,24 +57,20 @@ class TestRunner(ApiServerUnittest):
|
||||
"user_agent": "iOS/10.3",
|
||||
"device_sn": "HZfFBh6tU59EdXJ",
|
||||
"os_platform": "ios",
|
||||
"app_version": "2.8.6"
|
||||
"app_version": "2.8.6",
|
||||
},
|
||||
"json": {
|
||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||
}
|
||||
},
|
||||
},
|
||||
"validate": [
|
||||
{"check": "status_code", "expect": 200}
|
||||
]
|
||||
"validate": [{"check": "status_code", "expect": 200}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"project_mapping": {
|
||||
"functions": self.debugtalk_functions
|
||||
},
|
||||
"testcases": testcases
|
||||
"project_mapping": {"functions": self.debugtalk_functions},
|
||||
"testcases": testcases,
|
||||
}
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
@@ -107,7 +90,7 @@ class TestRunner(ApiServerUnittest):
|
||||
{
|
||||
"config": {
|
||||
"name": "basic test with httpbin",
|
||||
"base_url": HTTPBIN_SERVER
|
||||
"base_url": HTTPBIN_SERVER,
|
||||
},
|
||||
"teststeps": [
|
||||
{
|
||||
@@ -116,27 +99,18 @@ class TestRunner(ApiServerUnittest):
|
||||
"request": {
|
||||
"url": "/anything",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"user_agent": "iOS/10.3",
|
||||
"os_platform": "ios"
|
||||
},
|
||||
"data": "a=1&b=2"
|
||||
"headers": {"user_agent": "iOS/10.3", "os_platform": "ios"},
|
||||
"data": "a=1&b=2",
|
||||
},
|
||||
"setup_hooks": [
|
||||
{"total": "${sum_two(1, 5)}"}
|
||||
],
|
||||
"validate": [
|
||||
{"check": "status_code", "expect": 200}
|
||||
]
|
||||
"setup_hooks": [{"total": "${sum_two(1, 5)}"}],
|
||||
"validate": [{"check": "status_code", "expect": 200}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"project_mapping": {
|
||||
"functions": self.debugtalk_functions
|
||||
},
|
||||
"testcases": testcases
|
||||
"project_mapping": {"functions": self.debugtalk_functions},
|
||||
"testcases": testcases,
|
||||
}
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
@@ -151,7 +125,7 @@ class TestRunner(ApiServerUnittest):
|
||||
{
|
||||
"config": {
|
||||
"name": "basic test with httpbin",
|
||||
"base_url": HTTPBIN_SERVER
|
||||
"base_url": HTTPBIN_SERVER,
|
||||
},
|
||||
"teststeps": [
|
||||
{
|
||||
@@ -162,29 +136,25 @@ class TestRunner(ApiServerUnittest):
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"content-type": "application/json",
|
||||
"user_agent": "iOS/10.3"
|
||||
"user_agent": "iOS/10.3",
|
||||
},
|
||||
"json": {
|
||||
"os_platform": "ios",
|
||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||
}
|
||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad",
|
||||
},
|
||||
},
|
||||
"setup_hooks": [
|
||||
"${modify_request_json($request, android)}"
|
||||
],
|
||||
"setup_hooks": ["${modify_request_json($request, android)}"],
|
||||
"validate": [
|
||||
{"check": "status_code", "expect": 200},
|
||||
{"check": "content.json.os_platform", "expect": "android"}
|
||||
]
|
||||
{"check": "content.json.os_platform", "expect": "android"},
|
||||
],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"project_mapping": {
|
||||
"functions": self.debugtalk_functions
|
||||
},
|
||||
"testcases": testcases
|
||||
"project_mapping": {"functions": self.debugtalk_functions},
|
||||
"testcases": testcases,
|
||||
}
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
@@ -194,9 +164,7 @@ class TestRunner(ApiServerUnittest):
|
||||
def test_run_testcase_with_teardown_hooks_success(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
"name": "basic test with httpbin"
|
||||
},
|
||||
"config": {"name": "basic test with httpbin"},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "get token",
|
||||
@@ -208,25 +176,23 @@ class TestRunner(ApiServerUnittest):
|
||||
"user_agent": "iOS/10.3",
|
||||
"device_sn": "HZfFBh6tU59EdXJ",
|
||||
"os_platform": "ios",
|
||||
"app_version": "2.8.6"
|
||||
"app_version": "2.8.6",
|
||||
},
|
||||
"json": {
|
||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||
}
|
||||
},
|
||||
},
|
||||
"validate": [
|
||||
{"check": "status_code", "expect": 200}
|
||||
"validate": [{"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 = {
|
||||
"project_mapping": {
|
||||
"functions": self.debugtalk_functions
|
||||
},
|
||||
"testcases": testcases
|
||||
"project_mapping": {"functions": self.debugtalk_functions},
|
||||
"testcases": testcases,
|
||||
}
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
@@ -241,9 +207,7 @@ class TestRunner(ApiServerUnittest):
|
||||
def test_run_testcase_with_teardown_hooks_fail(self):
|
||||
testcases = [
|
||||
{
|
||||
"config": {
|
||||
"name": "basic test with httpbin"
|
||||
},
|
||||
"config": {"name": "basic test with httpbin"},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "get token",
|
||||
@@ -255,25 +219,23 @@ class TestRunner(ApiServerUnittest):
|
||||
"user_agent": "iOS/10.3",
|
||||
"device_sn": "HZfFBh6tU59EdXJ",
|
||||
"os_platform": "ios",
|
||||
"app_version": "2.8.6"
|
||||
"app_version": "2.8.6",
|
||||
},
|
||||
"json": {
|
||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||
}
|
||||
},
|
||||
},
|
||||
"validate": [
|
||||
{"check": "status_code", "expect": 404}
|
||||
"validate": [{"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 = {
|
||||
"project_mapping": {
|
||||
"functions": self.debugtalk_functions
|
||||
},
|
||||
"testcases": testcases
|
||||
"project_mapping": {"functions": self.debugtalk_functions},
|
||||
"testcases": testcases,
|
||||
}
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
@@ -287,7 +249,8 @@ class TestRunner(ApiServerUnittest):
|
||||
|
||||
def test_bugfix_type_match(self):
|
||||
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)
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
@@ -309,28 +272,38 @@ class TestRunner(ApiServerUnittest):
|
||||
"user_agent": "iOS/10.3",
|
||||
"device_sn": "HZfFBh6tU59EdXJ",
|
||||
"os_platform": "ios",
|
||||
"app_version": "2.8.6"
|
||||
"app_version": "2.8.6",
|
||||
},
|
||||
"json": {
|
||||
"sign": "5188962c489d1a35effa99e9346dd5efd4fdabad"
|
||||
}
|
||||
},
|
||||
},
|
||||
"validate": [
|
||||
{"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.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 = {
|
||||
"project_mapping": {
|
||||
"functions": self.debugtalk_functions
|
||||
},
|
||||
"testcases": testcases
|
||||
"project_mapping": {"functions": self.debugtalk_functions},
|
||||
"testcases": testcases,
|
||||
}
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
@@ -343,7 +316,7 @@ class TestRunner(ApiServerUnittest):
|
||||
"config": {
|
||||
"name": "basic test with httpbin",
|
||||
"base_url": HTTPBIN_SERVER,
|
||||
"variables": "${gen_variables()}"
|
||||
"variables": "${gen_variables()}",
|
||||
},
|
||||
"teststeps": [
|
||||
{
|
||||
@@ -352,24 +325,17 @@ class TestRunner(ApiServerUnittest):
|
||||
"request": {
|
||||
"url": "/anything",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"user_agent": "iOS/10.3",
|
||||
"os_platform": "ios"
|
||||
},
|
||||
"data": "a=1&b=2"
|
||||
"headers": {"user_agent": "iOS/10.3", "os_platform": "ios"},
|
||||
"data": "a=1&b=2",
|
||||
},
|
||||
"validate": [
|
||||
{"check": "status_code", "expect": 200}
|
||||
]
|
||||
"validate": [{"check": "status_code", "expect": 200}],
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
tests_mapping = {
|
||||
"project_mapping": {
|
||||
"functions": self.debugtalk_functions
|
||||
},
|
||||
"testcases": testcases
|
||||
"project_mapping": {"functions": self.debugtalk_functions},
|
||||
"testcases": testcases,
|
||||
}
|
||||
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||
parsed_testcase = parsed_testcases[0]
|
||||
|
||||
@@ -7,12 +7,9 @@ from tests.base import ApiServerUnittest
|
||||
|
||||
|
||||
class TestUtils(ApiServerUnittest):
|
||||
|
||||
def test_set_os_environ(self):
|
||||
self.assertNotIn("abc", os.environ)
|
||||
variables_mapping = {
|
||||
"abc": "123"
|
||||
}
|
||||
variables_mapping = {"abc": "123"}
|
||||
utils.set_os_environ(variables_mapping)
|
||||
self.assertIn("abc", os.environ)
|
||||
self.assertEqual(os.environ["abc"], "123")
|
||||
@@ -26,8 +23,8 @@ class TestUtils(ApiServerUnittest):
|
||||
"last_name": "Lee",
|
||||
},
|
||||
"age": 29,
|
||||
"cities": ["Guangzhou", "Shenzhen"]
|
||||
}
|
||||
"cities": ["Guangzhou", "Shenzhen"],
|
||||
},
|
||||
}
|
||||
query = "ids.2"
|
||||
result = utils.query_json(json_content, query)
|
||||
@@ -63,6 +60,7 @@ class TestUtils(ApiServerUnittest):
|
||||
|
||||
def current_validators(self):
|
||||
from httprunner.builtin import comparators
|
||||
|
||||
functions_mapping = loader.load.load_module_functions(comparators)
|
||||
|
||||
functions_mapping["equals"](None, None)
|
||||
@@ -80,17 +78,17 @@ class TestUtils(ApiServerUnittest):
|
||||
functions_mapping["not_equals"](123, "123")
|
||||
|
||||
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.
|
||||
functions_mapping["length_equals"]("123", '3')
|
||||
functions_mapping["length_equals"]("123", "3")
|
||||
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_or_equals"]("123", 3)
|
||||
|
||||
functions_mapping["contains"]("123abc456", "3ab")
|
||||
functions_mapping["contains"](['1', '2'], "1")
|
||||
functions_mapping["contains"]({'a':1, 'b':2}, "a")
|
||||
functions_mapping["contains"](["1", "2"], "1")
|
||||
functions_mapping["contains"]({"a": 1, "b": 2}, "a")
|
||||
functions_mapping["contained_by"]("3ab", "123abc456")
|
||||
|
||||
functions_mapping["regex_match"]("123abc456", "^123\w+456$")
|
||||
@@ -119,11 +117,8 @@ class TestUtils(ApiServerUnittest):
|
||||
"Request": {
|
||||
"url": "http://127.0.0.1:5000",
|
||||
"METHOD": "POST",
|
||||
"Headers": {
|
||||
"Accept": "application/json",
|
||||
"User-Agent": "ios/9.3"
|
||||
}
|
||||
}
|
||||
"Headers": {"Accept": "application/json", "User-Agent": "ios/9.3"},
|
||||
},
|
||||
}
|
||||
new_dict = utils.lower_test_dict_keys(origin_dict)
|
||||
self.assertIn("name", new_dict)
|
||||
@@ -133,10 +128,7 @@ class TestUtils(ApiServerUnittest):
|
||||
self.assertIn("Accept", new_dict["request"]["headers"])
|
||||
self.assertIn("User-Agent", new_dict["request"]["headers"])
|
||||
|
||||
origin_dict = {
|
||||
"Name": "test",
|
||||
"Request": "$default_request"
|
||||
}
|
||||
origin_dict = {"Name": "test", "Request": "$default_request"}
|
||||
new_dict = utils.lower_test_dict_keys(origin_dict)
|
||||
self.assertIn("$default_request", new_dict["request"])
|
||||
|
||||
@@ -144,10 +136,7 @@ class TestUtils(ApiServerUnittest):
|
||||
request_dict = {
|
||||
"url": "http://127.0.0.1:5000",
|
||||
"METHOD": "POST",
|
||||
"Headers": {
|
||||
"Accept": "application/json",
|
||||
"User-Agent": "ios/9.3"
|
||||
}
|
||||
"Headers": {"Accept": "application/json", "User-Agent": "ios/9.3"},
|
||||
}
|
||||
new_request_dict = utils.lower_dict_keys(request_dict)
|
||||
self.assertIn("method", new_request_dict)
|
||||
@@ -164,10 +153,7 @@ class TestUtils(ApiServerUnittest):
|
||||
self.assertEqual(None, request_dict)
|
||||
|
||||
def test_ensure_mapping_format(self):
|
||||
map_list = [
|
||||
{"a": 1},
|
||||
{"b": 2}
|
||||
]
|
||||
map_list = [{"a": 1}, {"b": 2}]
|
||||
ordered_dict = utils.ensure_mapping_format(map_list)
|
||||
self.assertIsInstance(ordered_dict, dict)
|
||||
self.assertIn("a", ordered_dict)
|
||||
@@ -175,7 +161,9 @@ class TestUtils(ApiServerUnittest):
|
||||
def test_extend_variables(self):
|
||||
raw_variables = [{"var1": "val1"}, {"var2": "val2"}]
|
||||
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["var2"], "val2")
|
||||
self.assertEqual(extended_variables_mapping["var3"], "val3")
|
||||
@@ -183,25 +171,27 @@ class TestUtils(ApiServerUnittest):
|
||||
def test_extend_variables_fix(self):
|
||||
raw_variables = [{"var1": "val1"}, {"var2": "val2"}]
|
||||
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")
|
||||
|
||||
def test_deepcopy_dict(self):
|
||||
data = {
|
||||
'a': 1,
|
||||
'b': [2, 4],
|
||||
'c': lambda x: x+1,
|
||||
'd': open('LICENSE'),
|
||||
'f': {
|
||||
'f1': {'a1': 2},
|
||||
'f2': io.open('LICENSE', 'rb'),
|
||||
}
|
||||
"a": 1,
|
||||
"b": [2, 4],
|
||||
"c": lambda x: x + 1,
|
||||
"d": open("LICENSE"),
|
||||
"f": {
|
||||
"f1": {"a1": 2},
|
||||
"f2": io.open("LICENSE", "rb"),
|
||||
},
|
||||
}
|
||||
new_data = utils.deepcopy_dict(data)
|
||||
data["a"] = 0
|
||||
self.assertEqual(new_data["a"], 1)
|
||||
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.assertEqual(id(new_data["c"]), id(data["c"]))
|
||||
# self.assertEqual(id(new_data["d"]), id(data["d"]))
|
||||
@@ -218,41 +208,24 @@ class TestUtils(ApiServerUnittest):
|
||||
shutil.rmtree(project_name)
|
||||
|
||||
def test_cartesian_product_one(self):
|
||||
parameters_content_list = [
|
||||
[
|
||||
{"a": 1},
|
||||
{"a": 2}
|
||||
]
|
||||
]
|
||||
parameters_content_list = [[{"a": 1}, {"a": 2}]]
|
||||
product_list = utils.gen_cartesian_product(*parameters_content_list)
|
||||
self.assertEqual(
|
||||
product_list,
|
||||
[
|
||||
{"a": 1},
|
||||
{"a": 2}
|
||||
]
|
||||
)
|
||||
self.assertEqual(product_list, [{"a": 1}, {"a": 2}])
|
||||
|
||||
def test_cartesian_product_multiple(self):
|
||||
parameters_content_list = [
|
||||
[
|
||||
{"a": 1},
|
||||
{"a": 2}
|
||||
],
|
||||
[
|
||||
{"x": 111, "y": 112},
|
||||
{"x": 121, "y": 122}
|
||||
]
|
||||
[{"a": 1}, {"a": 2}],
|
||||
[{"x": 111, "y": 112}, {"x": 121, "y": 122}],
|
||||
]
|
||||
product_list = utils.gen_cartesian_product(*parameters_content_list)
|
||||
self.assertEqual(
|
||||
product_list,
|
||||
[
|
||||
{'a': 1, 'x': 111, 'y': 112},
|
||||
{'a': 1, 'x': 121, 'y': 122},
|
||||
{'a': 2, 'x': 111, 'y': 112},
|
||||
{'a': 2, 'x': 121, 'y': 122}
|
||||
]
|
||||
{"a": 1, "x": 111, "y": 112},
|
||||
{"a": 1, "x": 121, "y": 122},
|
||||
{"a": 2, "x": 111, "y": 112},
|
||||
{"a": 2, "x": 121, "y": 122},
|
||||
],
|
||||
)
|
||||
|
||||
def test_cartesian_product_empty(self):
|
||||
@@ -261,15 +234,7 @@ class TestUtils(ApiServerUnittest):
|
||||
self.assertEqual(product_list, [])
|
||||
|
||||
def test_print_info(self):
|
||||
info_mapping = {
|
||||
"a": 1,
|
||||
"t": (1, 2),
|
||||
"b": {
|
||||
"b1": 123
|
||||
},
|
||||
"c": None,
|
||||
"d": [4, 5]
|
||||
}
|
||||
info_mapping = {"a": 1, "t": (1, 2), "b": {"b1": 123}, "c": None, "d": [4, 5]}
|
||||
utils.print_info(info_mapping)
|
||||
|
||||
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_mapping = {
|
||||
"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(
|
||||
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):
|
||||
@@ -289,19 +256,23 @@ class TestUtils(ApiServerUnittest):
|
||||
project_working_directory = os.path.join(os.getcwd(), "tests")
|
||||
project_mapping = {
|
||||
"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(
|
||||
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):
|
||||
project_working_directory = os.path.join(os.getcwd(), "tests")
|
||||
project_mapping = {
|
||||
"PWD": project_working_directory
|
||||
}
|
||||
project_mapping = {"PWD": project_working_directory}
|
||||
self.assertEqual(
|
||||
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