change: remove testsuite

This commit is contained in:
debugtalk
2022-04-23 15:04:35 +08:00
parent 24fcdfb418
commit 74299a5a5c
4 changed files with 71 additions and 191 deletions

View File

@@ -11,14 +11,13 @@ from loguru import logger
from pydantic import ValidationError
from httprunner import builtin, exceptions, utils
from httprunner.models import ProjectMeta, TestCase, TestSuite
from httprunner.models import ProjectMeta, TestCase
project_meta: Union[ProjectMeta, None] = None
def _load_yaml_file(yaml_file: Text) -> Dict:
""" load yaml file and check file content format
"""
"""load yaml file and check file content format"""
with open(yaml_file, mode="rb") as stream:
try:
yaml_content = yaml.load(stream, Loader=yaml.FullLoader)
@@ -31,8 +30,7 @@ def _load_yaml_file(yaml_file: Text) -> Dict:
def _load_json_file(json_file: Text) -> Dict:
""" load json file and check file content format
"""
"""load json file and check file content format"""
with open(json_file, mode="rb") as data_file:
try:
json_content = json.load(data_file)
@@ -81,20 +79,8 @@ def load_testcase_file(testcase_file: Text) -> TestCase:
return testcase_obj
def load_testsuite(testsuite: Dict) -> TestSuite:
path = testsuite["config"]["path"]
try:
# validate with pydantic TestCase model
testsuite_obj = TestSuite.parse_obj(testsuite)
except ValidationError as ex:
err_msg = f"TestSuite ValidationError:\nfile: {path}\nerror: {ex}"
raise exceptions.TestSuiteFormatError(err_msg)
return testsuite_obj
def load_dot_env_file(dot_env_path: Text) -> Dict:
""" load .env file.
"""load .env file.
Args:
dot_env_path (str): .env file path
@@ -140,7 +126,7 @@ def load_dot_env_file(dot_env_path: Text) -> Dict:
def load_csv_file(csv_file: Text) -> List[Dict]:
""" 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:
@@ -186,7 +172,7 @@ def load_csv_file(csv_file: Text) -> List[Dict]:
def load_folder_files(folder_path: Text, recursive: bool = True) -> List:
""" load folder path, return all files endswith .yml/.yaml/.json/_test.py in list.
"""load folder path, return all files endswith .yml/.yaml/.json/_test.py in list.
Args:
folder_path (str): specified folder path to load
@@ -227,7 +213,7 @@ def load_folder_files(folder_path: Text, recursive: bool = True) -> List:
def load_module_functions(module) -> Dict[Text, Callable]:
""" load python module functions.
"""load python module functions.
Args:
module: python module
@@ -251,13 +237,12 @@ def load_module_functions(module) -> Dict[Text, Callable]:
def load_builtin_functions() -> Dict[Text, Callable]:
""" load builtin module functions
"""
"""load builtin module functions"""
return load_module_functions(builtin)
def locate_file(start_path: Text, file_name: Text) -> Text:
""" locate filename and return absolute file path.
"""locate filename and return absolute file path.
searching will be recursive upward until system root dir.
Args:
@@ -295,7 +280,7 @@ def locate_file(start_path: Text, file_name: Text) -> Text:
def locate_debugtalk_py(start_path: Text) -> Text:
""" locate debugtalk.py file
"""locate debugtalk.py file
Args:
start_path (str): start locating path,
@@ -315,7 +300,7 @@ def locate_debugtalk_py(start_path: Text) -> Text:
def locate_project_root_directory(test_path: Text) -> Tuple[Text, Text]:
""" locate debugtalk.py path as project root directory
"""locate debugtalk.py path as project root directory
Args:
test_path: specified testfile path
@@ -352,7 +337,7 @@ def locate_project_root_directory(test_path: Text) -> Tuple[Text, Text]:
def load_debugtalk_functions() -> Dict[Text, Callable]:
""" load project debugtalk.py module functions
"""load project debugtalk.py module functions
debugtalk.py should be located in project root directory.
Returns:
@@ -376,7 +361,7 @@ def load_debugtalk_functions() -> Dict[Text, Callable]:
def load_project_meta(test_path: Text, reload: bool = False) -> ProjectMeta:
""" load testcases, .env, debugtalk.py functions.
"""load testcases, .env, debugtalk.py functions.
testcases folder is relative to project_root_directory
by default, project_meta will be loaded only once, unless set reload to true.
@@ -428,7 +413,7 @@ def load_project_meta(test_path: Text, reload: bool = False) -> ProjectMeta:
def convert_relative_project_root_dir(abs_path: Text) -> Text:
""" convert absolute path to relative path, based on project_meta.RootDir
"""convert absolute path to relative path, based on project_meta.RootDir
Args:
abs_path: absolute path
@@ -444,4 +429,4 @@ def convert_relative_project_root_dir(abs_path: Text) -> Text:
f"project_meta.RootDir: {_project_meta.RootDir}"
)
return abs_path[len(_project_meta.RootDir) + 1:]
return abs_path[len(_project_meta.RootDir) + 1 :]

View File

@@ -8,14 +8,21 @@ import jinja2
from loguru import logger
from httprunner import __version__, exceptions
from httprunner.compat import (convert_variables, ensure_path_sep,
ensure_testcase_v3, ensure_testcase_v3_api)
from httprunner.loader import (convert_relative_project_root_dir,
load_folder_files, load_project_meta,
load_test_file, load_testcase, load_testsuite)
from httprunner.compat import (
convert_variables,
ensure_path_sep,
ensure_testcase_v3,
ensure_testcase_v3_api,
)
from httprunner.loader import (
convert_relative_project_root_dir,
load_folder_files,
load_project_meta,
load_test_file,
load_testcase,
)
from httprunner.response import uniform_validator
from httprunner.utils import (ga_client, is_support_multiprocessing,
merge_variables)
from httprunner.utils import ga_client, is_support_multiprocessing
""" cache converted pytest files, avoid duplicate making
"""
@@ -71,10 +78,10 @@ if __name__ == "__main__":
def __ensure_absolute(path: Text) -> Text:
if path.startswith("./"):
# Linux/Darwin, hrun ./test.yml
path = path[len("./"):]
path = path[len("./") :]
elif path.startswith(".\\"):
# Windows, hrun .\\test.yml
path = path[len(".\\"):]
path = path[len(".\\") :]
path = ensure_path_sep(path)
project_meta = load_project_meta(path)
@@ -92,7 +99,7 @@ def __ensure_absolute(path: Text) -> Text:
def ensure_file_abs_path_valid(file_abs_path: Text) -> Text:
""" ensure file path valid for pytest, handle cases when directory name includes dot/hyphen/space
"""ensure file path valid for pytest, handle cases when directory name includes dot/hyphen/space
Args:
file_abs_path: absolute file path
@@ -133,8 +140,7 @@ def ensure_file_abs_path_valid(file_abs_path: Text) -> Text:
def __ensure_testcase_module(path: Text):
""" ensure pytest files are in python module, generate __init__.py on demand
"""
"""ensure pytest files are in python module, generate __init__.py on demand"""
init_file = os.path.join(os.path.dirname(path), "__init__.py")
if os.path.isfile(init_file):
return
@@ -431,61 +437,8 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text:
return testcase_python_abs_path
def make_testsuite(testsuite: Dict):
"""convert valid testsuite dict to pytest folder with testcases"""
# validate testsuite format
load_testsuite(testsuite)
testsuite_config = testsuite["config"]
testsuite_path = testsuite_config["path"]
testsuite_variables = convert_variables(
testsuite_config.get("variables", {}), testsuite_path
)
logger.info(f"start to make testsuite: {testsuite_path}")
# create directory with testsuite file name, put its testcases under this directory
testsuite_path = ensure_file_abs_path_valid(testsuite_path)
testsuite_dir, file_suffix = os.path.splitext(testsuite_path)
# demo_testsuite.yml => demo_testsuite_yml
testsuite_dir = f"{testsuite_dir}_{file_suffix.lstrip('.')}"
for testcase in testsuite["testcases"]:
# get referenced testcase content
testcase_file = testcase["testcase"]
testcase_path = __ensure_absolute(testcase_file)
testcase_dict = load_test_file(testcase_path)
testcase_dict.setdefault("config", {})
testcase_dict["config"]["path"] = testcase_path
# override testcase name
testcase_dict["config"]["name"] = testcase["name"]
# override base_url
base_url = testsuite_config.get("base_url") or testcase.get("base_url")
if base_url:
testcase_dict["config"]["base_url"] = base_url
# override verify
if "verify" in testsuite_config:
testcase_dict["config"]["verify"] = testsuite_config["verify"]
# override variables
# testsuite testcase variables > testsuite config variables
testcase_variables = convert_variables(
testcase.get("variables", {}), testcase_path
)
testcase_variables = merge_variables(testcase_variables, testsuite_variables)
# testsuite testcase variables > testcase config variables
testcase_dict["config"]["variables"] = convert_variables(
testcase_dict["config"].get("variables", {}), testcase_path
)
testcase_dict["config"]["variables"].update(testcase_variables)
# make testcase
testcase_pytest_path = make_testcase(testcase_dict, testsuite_dir)
pytest_files_run_set.add(testcase_pytest_path)
def __make(tests_path: Text):
""" make testcase(s) with testcase/testsuite/folder absolute path
"""make testcase(s) with testcase/folder absolute path
generated pytest file path will be cached in pytest_files_made_cache_mapping
Args:
@@ -526,13 +479,13 @@ def __make(tests_path: Text):
if "config" not in test_content:
logger.warning(
f"Invalid testcase/testsuite file: {test_file}\n"
f"Invalid testcase file: {test_file}\n"
f"reason: missing config part."
)
continue
elif not isinstance(test_content["config"], Dict):
logger.warning(
f"Invalid testcase/testsuite file: {test_file}\n"
f"Invalid testcase file: {test_file}\n"
f"reason: config should be dict type, got {test_content['config']}"
)
continue
@@ -540,33 +493,19 @@ def __make(tests_path: Text):
# ensure path absolute
test_content.setdefault("config", {})["path"] = test_file
# testcase
if "teststeps" in test_content:
try:
testcase_pytest_path = make_testcase(test_content)
pytest_files_run_set.add(testcase_pytest_path)
except exceptions.TestCaseFormatError as ex:
logger.warning(
f"Invalid testcase file: {test_file}\n{type(ex).__name__}: {ex}"
)
continue
# testsuite
elif "testcases" in test_content:
try:
make_testsuite(test_content)
except exceptions.TestSuiteFormatError as ex:
logger.warning(
f"Invalid testsuite file: {test_file}\n{type(ex).__name__}: {ex}"
)
continue
# invalid format
else:
if "teststeps" not in test_content:
logger.warning(f"Invalid testcase file: {test_file}")
# testcase
try:
testcase_pytest_path = make_testcase(test_content)
pytest_files_run_set.add(testcase_pytest_path)
except exceptions.TestCaseFormatError as ex:
logger.warning(
f"Invalid test file: {test_file}\n"
f"reason: file content is neither testcase nor testsuite"
f"Invalid testcase file: {test_file}\n{type(ex).__name__}: {ex}"
)
continue
def main_make(tests_paths: List[Text]) -> List[Text]:
@@ -594,10 +533,10 @@ def main_make(tests_paths: List[Text]) -> List[Text]:
def init_make_parser(subparsers):
""" make testcases: parse command line options and run commands.
"""
"""make testcases: parse command line options and run commands."""
parser = subparsers.add_parser(
"make", help="Convert YAML/JSON testcases to pytest cases.",
"make",
help="Convert YAML/JSON testcases to pytest cases.",
)
parser.add_argument(
"testcase_path", nargs="*", help="Specify YAML/JSON testcase file/folder path"

View File

@@ -73,7 +73,8 @@ from request_methods.request_with_functions_test import (
content,
)
self.assertIn(
".call(RequestWithFunctions)", content,
".call(RequestWithFunctions)",
content,
)
def test_make_testcase_folder(self):
@@ -94,9 +95,7 @@ from request_methods.request_with_functions_test import (
def test_ensure_file_path_valid(self):
self.assertEqual(
ensure_file_abs_path_valid(
os.path.join(self.data_dir, "a-b.c", "2 3.yml")
),
ensure_file_abs_path_valid(os.path.join(self.data_dir, "a-b.c", "2 3.yml")),
os.path.join(self.data_dir, "a_b_c", "T2_3.yml"),
)
loader.project_meta = None
@@ -113,67 +112,31 @@ from request_methods.request_with_functions_test import (
)
loader.project_meta = None
self.assertEqual(
ensure_file_abs_path_valid(os.getcwd()), os.getcwd(),
ensure_file_abs_path_valid(os.getcwd()),
os.getcwd(),
)
loader.project_meta = None
self.assertEqual(
ensure_file_abs_path_valid(
os.path.join(self.data_dir, ".csv")
),
ensure_file_abs_path_valid(os.path.join(self.data_dir, ".csv")),
os.path.join(self.data_dir, ".csv"),
)
def test_convert_testcase_path(self):
self.assertEqual(
convert_testcase_path(
os.path.join(self.data_dir, "a-b.c", "2 3.yml")
),
convert_testcase_path(os.path.join(self.data_dir, "a-b.c", "2 3.yml")),
(
os.path.join(self.data_dir, "a_b_c", "T2_3_test.py"),
"T23",
),
)
self.assertEqual(
convert_testcase_path(
os.path.join(self.data_dir, "a-b.c", "中文case.yml")
),
convert_testcase_path(os.path.join(self.data_dir, "a-b.c", "中文case.yml")),
(
os.path.join(self.data_dir, "a_b_c", "中文case_test.py"),
"中文Case",
),
)
def test_make_testsuite(self):
path = ["examples/postman_echo/request_methods/demo_testsuite.yml"]
testcase_python_list = main_make(path)
self.assertEqual(len(testcase_python_list), 2)
self.assertIn(
os.path.join(
os.getcwd(),
os.path.join(
"examples",
"postman_echo",
"request_methods",
"demo_testsuite_yml",
"request_with_functions_test.py",
),
),
testcase_python_list,
)
self.assertIn(
os.path.join(
os.getcwd(),
os.path.join(
"examples",
"postman_echo",
"request_methods",
"demo_testsuite_yml",
"request_with_testcase_reference_test.py",
),
),
testcase_python_list,
)
def test_make_config_chain_style(self):
config = {
"name": "request methods testcase: validate with functions",
@@ -190,7 +153,11 @@ from request_methods.request_with_functions_test import (
def test_make_teststep_chain_style(self):
step = {
"name": "get with params",
"variables": {"foo1": "bar1", "foo2": 123, "sum_v": "${sum_two(1, 2)}",},
"variables": {
"foo1": "bar1",
"foo2": 123,
"sum_v": "${sum_two(1, 2)}",
},
"request": {
"method": "GET",
"url": "/get",

View File

@@ -97,7 +97,9 @@ class ProjectMeta(BaseModel):
dot_env_path: Text = "" # .env file path
functions: FunctionsMapping = {} # functions defined in debugtalk.py
env: Env = {}
RootDir: Text = os.getcwd() # project root directory (ensure absolute), the path debugtalk.py located
RootDir: Text = (
os.getcwd()
) # project root directory (ensure absolute), the path debugtalk.py located
class TestsMapping(BaseModel):
@@ -166,21 +168,20 @@ class SessionData(BaseModel):
class StepResult(BaseModel):
"""teststep data, each step maybe corresponding to one request or one testcase"""
name: Text = "" # teststep name
step_type: Text = "" # teststep type, request or testcase
name: Text = "" # teststep name
step_type: Text = "" # teststep type, request or testcase
success: bool = False
data: Union[SessionData, List['StepResult']] = None
elapsed: float = 0.0 # teststep elapsed time
content_size: float = 0 # response content size
data: Union[SessionData, List["StepResult"]] = None
elapsed: float = 0.0 # teststep elapsed time
content_size: float = 0 # response content size
export_vars: VariablesMapping = {}
attachment: Text = "" # teststep attachment
attachment: Text = "" # teststep attachment
StepResult.update_forward_refs()
class IStep(object):
def name(self) -> str:
raise NotImplementedError
@@ -211,18 +212,6 @@ class PlatformInfo(BaseModel):
platform: Text
class TestCaseRef(BaseModel):
name: Text
base_url: Text = ""
testcase: Text
variables: VariablesMapping = {}
class TestSuite(BaseModel):
config: TConfig
testcases: List[TestCaseRef]
class Stat(BaseModel):
total: int = 0
success: int = 0