feat: convert YAML/JSON testcase to python testcase

This commit is contained in:
debugtalk
2020-05-14 16:46:52 +08:00
parent 9ba550bd9f
commit 202f040c1c
4 changed files with 267 additions and 80 deletions

View File

@@ -2,89 +2,81 @@ from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep
class TestCaseRequestMethodsWithVariables(TestCaseRunner):
config = TestsConfig(**{
"name": "request methods testcase with variables",
"variables": {
"foo1": "session_bar1"
},
"base_url": "https://postman-echo.com",
"verify": False
})
class TestCaseRequestWithVariables(TestCaseRunner):
config = TestsConfig(
**{
"name": "request methods testcase with variables",
"variables": {"foo1": "session_bar1"},
"base_url": "https://postman-echo.com",
"verify": False,
}
)
teststeps = [
TestStep(**{
"name": "get with params",
"variables": {
"foo1": "bar1",
"foo2": "session_bar2"
},
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$foo1",
"foo2": "$foo2"
TestStep(
**{
"name": "get with params",
"variables": {"foo1": "bar1", "foo2": "session_bar2"},
"request": {
"method": "GET",
"url": "/get",
"params": {"foo1": "$foo1", "foo2": "$foo2"},
"headers": {"User-Agent": "HttpRunner/3.0"},
},
"headers": {
"User-Agent": "HttpRunner/3.0"
}
},
"extract": {
"session_foo2": "body.args.foo2"
},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.args.foo1", "session_bar1"]},
{"eq": ["body.args.foo2", "session_bar2"]}
]
}),
TestStep(**{
"name": "post raw text",
"variables": {
"foo1": "hello world",
"foo3": "$session_foo2"
},
"request": {
"method": "POST",
"url": "/post",
"data": "This is expected to be sent back as part of response body: $foo1-$foo3.",
"headers": {
"User-Agent": "HttpRunner/3.0",
"Content-Type": "text/plain"
}
},
"validate": [
{"eq": ["status_code", 200]},
{"eq": [
"body.data",
"This is expected to be sent back as part of response body: session_bar1-session_bar2."
]},
]
}),
TestStep(**{
"name": "post form data",
"variables": {
"foo1": "session_bar1",
"foo2": "bar2"
},
"request": {
"method": "POST",
"url": "/post",
"data": "foo1=$foo1&foo2=$foo2",
"headers": {
"User-Agent": "HttpRunner/3.0",
"Content-Type": "application/x-www-form-urlencoded"
}
},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.form.foo1", "session_bar1"]},
{"eq": ["body.form.foo2", "bar2"]}
]
})
"extract": {"session_foo2": "body.args.foo2"},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.args.foo1", "session_bar1"]},
{"eq": ["body.args.foo2", "session_bar2"]},
],
}
),
TestStep(
**{
"name": "post raw text",
"variables": {"foo1": "hello world", "foo3": "$session_foo2"},
"request": {
"method": "POST",
"url": "/post",
"headers": {
"User-Agent": "HttpRunner/3.0",
"Content-Type": "text/plain",
},
"data": "This is expected to be sent back as part of response body: $foo1-$foo3.",
},
"validate": [
{"eq": ["status_code", 200]},
{
"eq": [
"body.data",
"This is expected to be sent back as part of response body: session_bar1-session_bar2.",
]
},
],
}
),
TestStep(
**{
"name": "post form data",
"variables": {"foo1": "bar1", "foo2": "bar2"},
"request": {
"method": "POST",
"url": "/post",
"headers": {
"User-Agent": "HttpRunner/3.0",
"Content-Type": "application/x-www-form-urlencoded",
},
"data": "foo1=$foo1&foo2=$foo2",
},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.form.foo1", "session_bar1"]},
{"eq": ["body.form.foo2", "bar2"]},
],
}
),
]
if __name__ == '__main__':
TestCaseRequestMethodsWithVariables().run()
if __name__ == "__main__":
TestCaseRequestWithVariables().run()

11
httprunner/loader_test.py Normal file
View File

@@ -0,0 +1,11 @@
import unittest
from httprunner.new_loader import load_testcase_file
class TestLoader(unittest.TestCase):
def test_load_testcase_file(self):
path = "examples/postman_echo/request_methods/request_with_variables.yml"
testcase = load_testcase_file(path)
self.assertEqual(testcase.config.name, "request methods testcase with variables")
self.assertEqual(len(testcase.teststeps), 3)

49
httprunner/make.py Normal file
View File

@@ -0,0 +1,49 @@
import os
import jinja2
from loguru import logger
from httprunner.new_loader import load_testcase_file
__TMPL__ = """
from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep
class {{ class_name }}(TestCaseRunner):
config = TestsConfig(**{{ config }})
teststeps = [
{% for teststep in teststeps %}
TestStep(**{{ teststep }}),
{% endfor %}
]
if __name__ == '__main__':
{{ class_name }}().run()
"""
def make_testcase(path: str) -> str:
testcase = load_testcase_file(path)
template = jinja2.Template(__TMPL__)
raw_file_name, _ = os.path.splitext(os.path.basename(path))
# convert title case, e.g. request_with_variables => RequestWithVariables
name_in_title_case = raw_file_name.title().replace("_", "")
data = {
"class_name": f"TestCase{name_in_title_case}",
"config": testcase["config"],
"teststeps": testcase["teststeps"],
}
content = template.render(data)
testcase_dir = os.path.dirname(path)
testcase_python_path = os.path.join(testcase_dir, f"{raw_file_name}_test.py")
with open(testcase_python_path, "w") as f:
f.write(content)
logger.info(f"generated testcase: {testcase_python_path}")
return testcase_python_path

135
httprunner/new_loader.py Normal file
View File

@@ -0,0 +1,135 @@
import io
import json
import os
import types
import yaml
from loguru import logger
from httprunner import builtin
from httprunner import exceptions
from httprunner.schema import TestCase
try:
# PyYAML version >= 5.1
# ref: https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation
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:
try:
yaml_content = yaml.load(stream)
except yaml.YAMLError as ex:
logger.error(str(ex))
raise exceptions.FileFormatError
return yaml_content
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:
try:
json_content = json.load(data_file)
except json.JSONDecodeError:
err_msg = f"JSONDecodeError: JSON file format error: {json_file}"
logger.error(err_msg)
raise exceptions.FileFormatError(err_msg)
return json_content
def load_testcase_file(testcase_file):
"""load testcase file and validate with pydantic model"""
file_suffix = os.path.splitext(testcase_file)[1].lower()
if file_suffix == ".json":
testcase_content = _load_json_file(testcase_file)
elif file_suffix in [".yaml", ".yml"]:
testcase_content = _load_yaml_file(testcase_file)
else:
# '' or other suffix
raise exceptions.FileFormatError(
f"testcase file should be YAML/JSON format, invalid testcase file: {testcase_file}"
)
# validate with pydantic TestCase model
TestCase.parse_obj(testcase_content)
return testcase_content
def load_folder_files(folder_path, recursive=True):
""" load folder path, return all files endswith yml/yaml/json in list.
Args:
folder_path (str): specified folder path to load
recursive (bool): load files recursively if True
Returns:
list: files endswith yml/yaml/json
"""
if isinstance(folder_path, (list, set)):
files = []
for path in set(folder_path):
files.extend(load_folder_files(path, recursive))
return files
if not os.path.exists(folder_path):
return []
file_list = []
for dirpath, dirnames, filenames in os.walk(folder_path):
filenames_list = []
for filename in filenames:
if not filename.endswith((".yml", ".yaml", ".json")):
continue
filenames_list.append(filename)
for filename in filenames_list:
file_path = os.path.join(dirpath, filename)
file_list.append(file_path)
if not recursive:
break
return file_list
def load_module_functions(module):
""" load python module functions.
Args:
module: python module
Returns:
dict: functions mapping for specified python module
{
"func1_name": func1,
"func2_name": func2
}
"""
module_functions = {}
for name, item in vars(module).items():
if isinstance(item, types.FunctionType):
module_functions[name] = item
return module_functions
def load_builtin_functions():
""" load builtin module functions
"""
return load_module_functions(builtin)