change: generate pytest in chain style by default

This commit is contained in:
debugtalk
2020-06-03 15:49:20 +08:00
parent 6fa680502e
commit 3bc988ac1e
7 changed files with 86 additions and 163 deletions

View File

@@ -1,87 +1,69 @@
# NOTICE: Generated By HttpRunner. DO NOT EDIT! # NOTICE: Generated By HttpRunner. DO NOT EDIT!
# FROM: examples/postman_echo/request_methods/request_with_functions.yml # FROM: examples/postman_echo/request_methods/request_with_functions.yml
from httprunner import HttpRunner, TConfig, TStep from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
class TestCaseRequestWithFunctions(HttpRunner): class TestCaseRequestWithFunctions(HttpRunner):
config = TConfig( config = (
**{ Config("request methods testcase with functions")
"name": "request methods testcase with functions", .variables(**{"foo1": "session_bar1"})
"variables": {"foo1": "session_bar1"}, .base_url("https://postman-echo.com")
"base_url": "https://postman-echo.com", .verify(False)
"verify": False,
"path": "examples/postman_echo/request_methods/request_with_functions_test.py",
}
) )
teststeps = [ teststeps = [
TStep( Step(
**{ RunRequest("get with params")
"name": "get with params", .with_variables(
"variables": { **{"foo1": "bar1", "foo2": "session_bar2", "sum_v": "${sum_two(1, 2)}"}
"foo1": "bar1", )
"foo2": "session_bar2", .get("/get")
"sum_v": "${sum_two(1, 2)}", .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
}, .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"})
"request": { .extract()
"method": "GET", .with_jmespath("body.args.foo2", "session_foo2")
"url": "/get", .validate()
"params": {"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"}, .assert_equal("status_code", 200)
"headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, .assert_equal("body.args.foo1", "session_bar1")
}, .assert_equal("body.args.sum_v", "3")
"extract": {"session_foo2": "body.args.foo2"}, .assert_equal("body.args.foo2", "session_bar2")
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.args.foo1", "session_bar1"]},
{"eq": ["body.args.sum_v", "3"]},
{"eq": ["body.args.foo2", "session_bar2"]},
],
}
), ),
TStep( Step(
**{ RunRequest("post raw text")
"name": "post raw text", .with_variables(**{"foo1": "hello world", "foo3": "$session_foo2"})
"variables": {"foo1": "hello world", "foo3": "$session_foo2"}, .post("/post")
"request": { .with_headers(
"method": "POST", **{
"url": "/post", "User-Agent": "HttpRunner/${get_httprunner_version()}",
"headers": { "Content-Type": "text/plain",
"User-Agent": "HttpRunner/${get_httprunner_version()}", }
"Content-Type": "text/plain", )
}, .with_data(
"data": "This is expected to be sent back as part of response body: $foo1-$foo3.", "This is expected to be sent back as part of response body: $foo1-$foo3."
}, )
"validate": [ .validate()
{"eq": ["status_code", 200]}, .assert_equal("status_code", 200)
{ .assert_equal(
"eq": [ "body.data",
"body.data", "This is expected to be sent back as part of response body: session_bar1-session_bar2.",
"This is expected to be sent back as part of response body: session_bar1-session_bar2.", )
]
},
],
}
), ),
TStep( Step(
**{ RunRequest("post form data")
"name": "post form data", .with_variables(**{"foo1": "bar1", "foo2": "bar2"})
"variables": {"foo1": "bar1", "foo2": "bar2"}, .post("/post")
"request": { .with_headers(
"method": "POST", **{
"url": "/post", "User-Agent": "HttpRunner/${get_httprunner_version()}",
"headers": { "Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "HttpRunner/${get_httprunner_version()}", }
"Content-Type": "application/x-www-form-urlencoded", )
}, .with_data("foo1=$foo1&foo2=$foo2")
"data": "foo1=$foo1&foo2=$foo2", .validate()
}, .assert_equal("status_code", 200)
"validate": [ .assert_equal("body.form.foo1", "session_bar1")
{"eq": ["status_code", 200]}, .assert_equal("body.form.foo2", "bar2")
{"eq": ["body.form.foo1", "session_bar1"]},
{"eq": ["body.form.foo2", "bar2"]},
],
}
), ),
] ]

View File

@@ -23,14 +23,9 @@ def main_run(extra_args):
# keep compatibility with v2 # keep compatibility with v2
extra_args = ensure_cli_args(extra_args) extra_args = ensure_cli_args(extra_args)
chain_style = False
tests_path_list = [] tests_path_list = []
extra_args_new = [] extra_args_new = []
for item in extra_args: for item in extra_args:
if item == "--chain-style":
chain_style = True
continue
if not os.path.exists(item): if not os.path.exists(item):
# item is not file/folder path # item is not file/folder path
extra_args_new.append(item) extra_args_new.append(item)
@@ -43,7 +38,7 @@ def main_run(extra_args):
logger.error(f"No valid testcase path in cli arguments: {extra_args}") logger.error(f"No valid testcase path in cli arguments: {extra_args}")
sys.exit(1) sys.exit(1)
testcase_path_list = main_make(tests_path_list, chain_style=chain_style) testcase_path_list = main_make(tests_path_list)
if not testcase_path_list: if not testcase_path_list:
logger.error("No valid testcases found, exit 1.") logger.error("No valid testcases found, exit 1.")
sys.exit(1) sys.exit(1)
@@ -115,7 +110,7 @@ def main():
elif sys.argv[1] == "har2case": elif sys.argv[1] == "har2case":
main_har2case(args) main_har2case(args)
elif sys.argv[1] == "make": elif sys.argv[1] == "make":
main_make(args.testcase_path, chain_style=args.chain_style) main_make(args.testcase_path)
def main_hrun_alias(): def main_hrun_alias():

View File

@@ -29,35 +29,6 @@ __TEMPLATE__ = jinja2.Template(
import os import os
import sys import sys
sys.path.insert(0, os.getcwd())
{% endif %}
from httprunner import HttpRunner, TConfig, TStep
{% for import_str in imports_list %}
{{ import_str }}
{% endfor %}
class {{ class_name }}(HttpRunner):
config = TConfig(**{{ config }})
teststeps = [
{% for teststep in teststeps %}
TStep(**{{ teststep }}),
{% endfor %}
]
if __name__ == "__main__":
{{ class_name }}().test_start()
"""
)
__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()) sys.path.insert(0, os.getcwd())
{% endif %} {% endif %}
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
@@ -231,7 +202,7 @@ def make_teststep_chain_style(teststep: Dict) -> Text:
if teststep.get("request"): if teststep.get("request"):
step_info += make_request_chain_style(teststep["request"]) step_info += make_request_chain_style(teststep["request"])
elif teststep.get("testcase"): elif teststep.get("testcase"):
testcase = teststep["testcase"].replace("CLS_LB(", "").replace(")CLS_RB", "") testcase = teststep["testcase"]
call_ref_testcase = f".call({testcase})" call_ref_testcase = f".call({testcase})"
step_info += call_ref_testcase step_info += call_ref_testcase
@@ -248,19 +219,21 @@ def make_teststep_chain_style(teststep: Dict) -> Text:
validator = uniform_validator(v) validator = uniform_validator(v)
assert_method = validator["assert"] assert_method = validator["assert"]
check = validator["check"] check = validator["check"]
if '"' in check:
# e.g. body."user-agent" => 'body."user-agent"'
check = f"'{check}'"
else:
check = f'"{check}"'
expect = validator["expect"] expect = validator["expect"]
if isinstance(expect, Text): if isinstance(expect, Text):
expect = f'"{expect}"' expect = f'"{expect}"'
step_info += f'.assert_{assert_method}("{check}", {expect})' step_info += f'.assert_{assert_method}({check}, {expect})'
return f"Step({step_info})" return f"Step({step_info})"
def __make_testcase( def __make_testcase(
testcase: Dict, testcase: Dict, dir_path: Text = None, ref_flag: bool = False,
dir_path: Text = None,
ref_flag: bool = False,
chain_style: bool = False,
) -> NoReturn: ) -> NoReturn:
"""convert valid testcase dict to pytest file path""" """convert valid testcase dict to pytest file path"""
# ensure compatibility with testcase format v2 # ensure compatibility with testcase format v2
@@ -309,7 +282,7 @@ def __make_testcase(
ref_testcase_python_path, ref_testcase_cls_name = convert_testcase_path( ref_testcase_python_path, ref_testcase_cls_name = convert_testcase_path(
ref_testcase_path ref_testcase_path
) )
teststep["testcase"] = f"CLS_LB({ref_testcase_cls_name})CLS_RB" teststep["testcase"] = ref_testcase_cls_name
# prepare import ref testcase # prepare import ref testcase
ref_testcase_python_path = ref_testcase_python_path[len(os.getcwd()) + 1 :] ref_testcase_python_path = ref_testcase_python_path[len(os.getcwd()) + 1 :]
@@ -323,19 +296,12 @@ def __make_testcase(
"testcase_path": __ensure_cwd_relative(testcase_path), "testcase_path": __ensure_cwd_relative(testcase_path),
"class_name": f"TestCase{testcase_cls_name}", "class_name": f"TestCase{testcase_cls_name}",
"imports_list": imports_list, "imports_list": imports_list,
} "config_chain_style": make_config_chain_style(config),
"teststeps_chain_style": [
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 make_teststep_chain_style(step) for step in teststeps
] ],
content = __TEMPLATE_CHAIN_STYLE__.render(data) }
else: content = __TEMPLATE__.render(data)
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: with open(testcase_python_path, "w", encoding="utf-8") as f:
f.write(content) f.write(content)
@@ -348,7 +314,7 @@ def __make_testcase(
make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path)) make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path))
def __make_testsuite(testsuite: Dict, chain_style: bool = False) -> NoReturn: def __make_testsuite(testsuite: Dict) -> NoReturn:
"""convert valid testsuite dict to pytest folder with testcases""" """convert valid testsuite dict to pytest folder with testcases"""
# validate testsuite format # validate testsuite format
load_testsuite(testsuite) load_testsuite(testsuite)
@@ -393,12 +359,10 @@ def __make_testsuite(testsuite: Dict, chain_style: bool = False) -> NoReturn:
testcase_dict["config"]["variables"].update(testsuite_variables) testcase_dict["config"]["variables"].update(testsuite_variables)
# make testcase # make testcase
__make_testcase(testcase_dict, testsuite_dir, chain_style=chain_style) __make_testcase(testcase_dict, testsuite_dir)
def __make( def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn:
tests_path: Text, ref_flag: bool = False, chain_style: bool = False
) -> NoReturn:
""" make testcase(s) with testcase/testsuite/folder absolute path """ make testcase(s) with testcase/testsuite/folder absolute path
generated pytest file path will be cached in make_files_cache_set generated pytest file path will be cached in make_files_cache_set
@@ -432,16 +396,14 @@ def __make(
# testcase # testcase
if "teststeps" in test_content: if "teststeps" in test_content:
try: try:
__make_testcase( __make_testcase(test_content, ref_flag=ref_flag)
test_content, ref_flag=ref_flag, chain_style=chain_style
)
except exceptions.TestCaseFormatError: except exceptions.TestCaseFormatError:
continue continue
# testsuite # testsuite
elif "testcases" in test_content: elif "testcases" in test_content:
try: try:
__make_testsuite(test_content, chain_style=chain_style) __make_testsuite(test_content)
except exceptions.TestSuiteFormatError: except exceptions.TestSuiteFormatError:
continue continue
@@ -452,12 +414,12 @@ def __make(
) )
def main_make(tests_paths: List[Text], chain_style: bool = False) -> List[Text]: def main_make(tests_paths: List[Text]) -> List[Text]:
for tests_path in tests_paths: for tests_path in tests_paths:
if not os.path.isabs(tests_path): if not os.path.isabs(tests_path):
tests_path = os.path.join(os.getcwd(), tests_path) tests_path = os.path.join(os.getcwd(), tests_path)
__make(tests_path, chain_style=chain_style) __make(tests_path)
testcase_path_list = list(make_files_cache_set) testcase_path_list = list(make_files_cache_set)
__format_pytest_with_black(testcase_path_list) __format_pytest_with_black(testcase_path_list)
@@ -473,11 +435,5 @@ def init_make_parser(subparsers):
parser.add_argument( parser.add_argument(
"testcase_path", nargs="*", help="Specify YAML/JSON testcase file/folder path" "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 return parser

View File

@@ -2,7 +2,7 @@ import os
import time import time
import uuid import uuid
from datetime import datetime from datetime import datetime
from typing import List, Dict, Text, NoReturn, Union from typing import List, Dict, Text, NoReturn
try: try:
import allure import allure
@@ -35,8 +35,8 @@ from httprunner.schema import (
class HttpRunner(object): class HttpRunner(object):
config: Union[TConfig, Config] config: Config
teststeps: List[Union[TStep, Step]] teststeps: List[Step]
success: bool = True # indicate testcase execution result success: bool = True # indicate testcase execution result
__config: TConfig __config: TConfig
@@ -53,21 +53,10 @@ class HttpRunner(object):
__log_path: Text = "" __log_path: Text = ""
def __init_tests__(self) -> NoReturn: def __init_tests__(self) -> NoReturn:
if isinstance(self.config, TConfig): self.__config = self.config.perform()
self.__config = self.config
elif isinstance(self.config, Config):
self.__config = self.config.perform()
else:
raise exceptions.TestCaseFormatError(f"config type error: {self.config}")
self.__teststeps = [] self.__teststeps = []
for step in self.teststeps: for step in self.teststeps:
if isinstance(step, TStep): self.__teststeps.append(step.perform())
self.__teststeps.append(step)
elif isinstance(step, Step):
self.__teststeps.append(step.perform())
else:
raise exceptions.TestCaseFormatError(f"step type error: {step}")
def with_project_meta(self, project_meta: ProjectMeta) -> "HttpRunner": def with_project_meta(self, project_meta: ProjectMeta) -> "HttpRunner":
self.__project_meta = project_meta self.__project_meta = project_meta

View File

@@ -66,7 +66,7 @@ teststeps:
validate: validate:
- eq: ["status_code", 200] - eq: ["status_code", 200]
- eq: ["body.args.foo1", "session_bar1"] - eq: ["body.args.foo1", "session_bar1"]
- eq: ["body.args.sum_v", 3] - eq: ["body.args.sum_v", "3"]
- eq: ["body.args.foo2", "session_bar2"] - eq: ["body.args.foo2", "session_bar2"]
- -
name: post raw text name: post raw text

View File

@@ -189,6 +189,7 @@ class RunTestCase(object):
def call(self, testcase: Callable): def call(self, testcase: Callable):
self.__t_step.testcase = testcase self.__t_step.testcase = testcase
return self
def perform(self) -> TStep: def perform(self) -> TStep:
return self.__t_step return self.__t_step

View File

@@ -43,7 +43,7 @@ from examples.postman_echo.request_methods.request_with_functions_test import (
content, content,
) )
self.assertIn( self.assertIn(
'"testcase": RequestWithFunctions,', content, '.call(RequestWithFunctions)', content,
) )
def test_make_testcase_folder(self): def test_make_testcase_folder(self):