mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-01 05:49:40 +08:00
feat: make pytest files in chain style
This commit is contained in:
@@ -16,6 +16,7 @@ from httprunner.loader import (
|
||||
load_project_meta,
|
||||
)
|
||||
from httprunner.parser import parse_data
|
||||
from httprunner.response import uniform_validator
|
||||
|
||||
""" cache converted pytest files, avoid duplicate making
|
||||
"""
|
||||
@@ -50,6 +51,35 @@ if __name__ == "__main__":
|
||||
"""
|
||||
)
|
||||
|
||||
__TEMPLATE_CHAIN_STYLE__ = jinja2.Template(
|
||||
"""# NOTICE: Generated By HttpRunner. DO NOT EDIT!
|
||||
# FROM: {{ testcase_path }}
|
||||
{% if imports_list %}
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.getcwd())
|
||||
{% endif %}
|
||||
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
|
||||
{% for import_str in imports_list %}
|
||||
{{ import_str }}
|
||||
{% endfor %}
|
||||
|
||||
class {{ class_name }}(HttpRunner):
|
||||
config = {{ config_chain_style }}
|
||||
|
||||
teststeps = [
|
||||
{% for step_chain_style in teststeps_chain_style %}
|
||||
{{ step_chain_style }},
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
{{ class_name }}().test_start()
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def __ensure_file_name(path: Text) -> Text:
|
||||
""" ensure file name not startswith digit
|
||||
@@ -96,7 +126,7 @@ def __ensure_testcase_module(path: Text) -> NoReturn:
|
||||
return
|
||||
|
||||
with open(init_file, "w", encoding="utf-8") as f:
|
||||
f.write("# NOTICE: Generated By HttpRunner. DO NOT EDIT!")
|
||||
f.write("# NOTICE: Generated By HttpRunner. DO NOT EDIT!\n")
|
||||
|
||||
|
||||
def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]:
|
||||
@@ -132,8 +162,105 @@ def __format_pytest_with_black(python_paths: List[Text]) -> NoReturn:
|
||||
logger.error(ex)
|
||||
|
||||
|
||||
def make_config_chain_style(config: Dict) -> Text:
|
||||
config_chain_style = f'Config("{config["name"]}")'
|
||||
|
||||
if config["variables"]:
|
||||
variables = config["variables"]
|
||||
config_chain_style += f".variables(**{variables})"
|
||||
|
||||
if "base_url" in config:
|
||||
config_chain_style += f'.base_url("{config["base_url"]}")'
|
||||
|
||||
if "verify" in config:
|
||||
config_chain_style += f'.verify({config["verify"]})'
|
||||
|
||||
return config_chain_style
|
||||
|
||||
|
||||
def make_request_chain_style(request: Dict) -> Text:
|
||||
method = request["method"].lower()
|
||||
url = request["url"]
|
||||
request_chain_style = f'.{method}("{url}")'
|
||||
|
||||
if "params" in request:
|
||||
params = request["params"]
|
||||
request_chain_style += f".with_params(**{params})"
|
||||
|
||||
if "headers" in request:
|
||||
headers = request["headers"]
|
||||
request_chain_style += f".with_headers(**{headers})"
|
||||
|
||||
if "cookies" in request:
|
||||
cookies = request["cookies"]
|
||||
request_chain_style += f".with_cookies(**{cookies})"
|
||||
|
||||
if "data" in request:
|
||||
data = request["data"]
|
||||
if isinstance(data, Text):
|
||||
data = f'"{data}"'
|
||||
request_chain_style += f".with_data({data})"
|
||||
|
||||
if "timeout" in request:
|
||||
timeout = request["timeout"]
|
||||
request_chain_style += f".set_timeout({timeout})"
|
||||
|
||||
if "verify" in request:
|
||||
verify = request["verify"]
|
||||
request_chain_style += f".set_verify({verify})"
|
||||
|
||||
if "allow_redirects" in request:
|
||||
allow_redirects = request["allow_redirects"]
|
||||
request_chain_style += f".set_allow_redirects({allow_redirects})"
|
||||
|
||||
return request_chain_style
|
||||
|
||||
|
||||
def make_teststep_chain_style(teststep: Dict) -> Text:
|
||||
if teststep.get("request"):
|
||||
step_info = f'RunRequest("{teststep["name"]}")'
|
||||
elif teststep.get("testcase"):
|
||||
step_info = f'RunTestCase("{teststep["name"]}")'
|
||||
else:
|
||||
raise exceptions.TestCaseFormatError
|
||||
|
||||
if "variables" in teststep:
|
||||
variables = teststep["variables"]
|
||||
step_info += f".with_variables(**{variables})"
|
||||
|
||||
if teststep.get("request"):
|
||||
step_info += make_request_chain_style(teststep["request"])
|
||||
elif teststep.get("testcase"):
|
||||
testcase = teststep["testcase"].replace("CLS_LB(", "").replace(")CLS_RB", "")
|
||||
call_ref_testcase = f".call({testcase})"
|
||||
step_info += call_ref_testcase
|
||||
|
||||
if "extract" in teststep:
|
||||
step_info += ".extract()"
|
||||
|
||||
for extract_name, extract_path in teststep["extract"].items():
|
||||
step_info += f'.with_jmespath("{extract_path}", "{extract_name}")'
|
||||
|
||||
if "validate" in teststep:
|
||||
step_info += ".validate()"
|
||||
|
||||
for v in teststep["validate"]:
|
||||
validator = uniform_validator(v)
|
||||
assert_method = validator["assert"]
|
||||
check = validator["check"]
|
||||
expect = validator["expect"]
|
||||
if isinstance(expect, Text):
|
||||
expect = f'"{expect}"'
|
||||
step_info += f'.assert_{assert_method}("{check}", {expect})'
|
||||
|
||||
return f"Step({step_info})"
|
||||
|
||||
|
||||
def __make_testcase(
|
||||
testcase: Dict, dir_path: Text = None, ref_flag: bool = False
|
||||
testcase: Dict,
|
||||
dir_path: Text = None,
|
||||
ref_flag: bool = False,
|
||||
chain_style: bool = False,
|
||||
) -> NoReturn:
|
||||
"""convert valid testcase dict to pytest file path"""
|
||||
# ensure compatibility with testcase format v2
|
||||
@@ -195,12 +322,20 @@ def __make_testcase(
|
||||
data = {
|
||||
"testcase_path": __ensure_cwd_relative(testcase_path),
|
||||
"class_name": f"TestCase{testcase_cls_name}",
|
||||
"config": config,
|
||||
"teststeps": teststeps,
|
||||
"imports_list": imports_list,
|
||||
}
|
||||
content = __TEMPLATE__.render(data)
|
||||
content = content.replace("'CLS_LB(", "").replace(")CLS_RB'", "")
|
||||
|
||||
if chain_style:
|
||||
data["config_chain_style"] = make_config_chain_style(config)
|
||||
data["teststeps_chain_style"] = [
|
||||
make_teststep_chain_style(step) for step in teststeps
|
||||
]
|
||||
content = __TEMPLATE_CHAIN_STYLE__.render(data)
|
||||
else:
|
||||
data["config"] = config
|
||||
data["teststeps"] = teststeps
|
||||
content = __TEMPLATE__.render(data)
|
||||
content = content.replace("'CLS_LB(", "").replace(")CLS_RB'", "")
|
||||
|
||||
with open(testcase_python_path, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
@@ -213,7 +348,7 @@ def __make_testcase(
|
||||
make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path))
|
||||
|
||||
|
||||
def __make_testsuite(testsuite: Dict) -> NoReturn:
|
||||
def __make_testsuite(testsuite: Dict, chain_style: bool = False) -> NoReturn:
|
||||
"""convert valid testsuite dict to pytest folder with testcases"""
|
||||
# validate testsuite format
|
||||
load_testsuite(testsuite)
|
||||
@@ -258,10 +393,12 @@ def __make_testsuite(testsuite: Dict) -> NoReturn:
|
||||
testcase_dict["config"]["variables"].update(testsuite_variables)
|
||||
|
||||
# make testcase
|
||||
__make_testcase(testcase_dict, testsuite_dir)
|
||||
__make_testcase(testcase_dict, testsuite_dir, chain_style=chain_style)
|
||||
|
||||
|
||||
def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn:
|
||||
def __make(
|
||||
tests_path: Text, ref_flag: bool = False, chain_style: bool = False
|
||||
) -> NoReturn:
|
||||
""" make testcase(s) with testcase/testsuite/folder absolute path
|
||||
generated pytest file path will be cached in make_files_cache_set
|
||||
|
||||
@@ -295,14 +432,16 @@ def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn:
|
||||
# testcase
|
||||
if "teststeps" in test_content:
|
||||
try:
|
||||
__make_testcase(test_content, ref_flag=ref_flag)
|
||||
__make_testcase(
|
||||
test_content, ref_flag=ref_flag, chain_style=chain_style
|
||||
)
|
||||
except exceptions.TestCaseFormatError:
|
||||
continue
|
||||
|
||||
# testsuite
|
||||
elif "testcases" in test_content:
|
||||
try:
|
||||
__make_testsuite(test_content)
|
||||
__make_testsuite(test_content, chain_style=chain_style)
|
||||
except exceptions.TestSuiteFormatError:
|
||||
continue
|
||||
|
||||
@@ -313,12 +452,12 @@ def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn:
|
||||
)
|
||||
|
||||
|
||||
def main_make(tests_paths: List[Text]) -> List[Text]:
|
||||
def main_make(tests_paths: List[Text], chain_style: bool = False) -> List[Text]:
|
||||
for tests_path in tests_paths:
|
||||
if not os.path.isabs(tests_path):
|
||||
tests_path = os.path.join(os.getcwd(), tests_path)
|
||||
|
||||
__make(tests_path)
|
||||
__make(tests_path, chain_style=chain_style)
|
||||
|
||||
testcase_path_list = list(make_files_cache_set)
|
||||
__format_pytest_with_black(testcase_path_list)
|
||||
@@ -334,5 +473,11 @@ def init_make_parser(subparsers):
|
||||
parser.add_argument(
|
||||
"testcase_path", nargs="*", help="Specify YAML/JSON testcase file/folder path"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--chain-style",
|
||||
dest="chain_style",
|
||||
action="store_true",
|
||||
help="Convert pytest files in chain-style.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
Reference in New Issue
Block a user