diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 16044001..ab4e98c9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 3.0.6 (2020-05-23) +## 3.0.6 (2020-05-25) **Added** diff --git a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py index 887c0d28..7319ebaa 100644 --- a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner. DO'NOT EDIT! -# FROM: examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions.yml +# FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, TConfig, TStep diff --git a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py index 7b9d121b..a7647e8a 100644 --- a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner. DO'NOT EDIT! -# FROM: examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference.yml +# FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml from httprunner import HttpRunner, TConfig, TStep from examples.postman_echo.request_methods.request_with_functions_test import ( diff --git a/httprunner/ext/make/__init__.py b/httprunner/ext/make/__init__.py index 71c7c52b..8af9b58f 100644 --- a/httprunner/ext/make/__init__.py +++ b/httprunner/ext/make/__init__.py @@ -1,6 +1,6 @@ import os import subprocess -from typing import Union, Text, List, Tuple, Dict +from typing import Text, List, Tuple, Dict, Set, NoReturn import jinja2 from loguru import logger @@ -17,7 +17,7 @@ from httprunner.parser import parse_data """ cache converted pytest files, avoid duplicate making """ -make_files_cache_mapping: Dict[Text, List] = {} +make_files_cache_set: Set = set() __TEMPLATE__ = jinja2.Template( """# NOTICE: Generated By HttpRunner. DO'NOT EDIT! @@ -44,6 +44,32 @@ if __name__ == "__main__": ) +def __ensure_absolute(path: Text) -> Text: + project_meta = load_project_meta(path) + + if os.path.isabs(path): + absolute_path = path + else: + absolute_path = os.path.join(project_meta.PWD, path) + + return absolute_path + + +def __ensure_cwd_relative(path: Text) -> Text: + """ convert absolute path to relative path, based on os.getcwd() + + Args: + path: absolute path + + Returns: relative path based on os.getcwd() + + """ + if os.path.isabs(path): + return path[len(os.getcwd()) + 1 :] + else: + return path + + def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: """convert single YAML/JSON testcase path to python file""" if os.path.isdir(testcase_path): @@ -69,7 +95,7 @@ def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: return testcase_python_path, testcase_cls_name -def format_pytest_with_black(python_paths: List[Text]): +def __format_pytest_with_black(python_paths: List[Text]) -> NoReturn: logger.info("format pytest cases with black ...") try: subprocess.run(["black", *python_paths]) @@ -77,7 +103,7 @@ def format_pytest_with_black(python_paths: List[Text]): logger.error(ex) -def make_testcase(testcase: Dict) -> Text: +def __make_testcase(testcase: Dict, dir_path: Text = None) -> NoReturn: """convert valid testcase dict to pytest file path""" try: # validate testcase format @@ -86,16 +112,21 @@ def make_testcase(testcase: Dict) -> Text: logger.error(f"TestCaseFormatError: {ex}") raise - testcase_path = testcase["config"]["path"] + testcase_path = __ensure_absolute(testcase["config"]["path"]) logger.info(f"start to make testcase: {testcase_path}") - # convert abs path to relative - if os.path.isabs(testcase_path): - testcase_path = testcase_path[len(os.getcwd()) + 1 :] testcase_python_path, testcase_cls_name = convert_testcase_path(testcase_path) + if dir_path: + testcase_python_path = os.path.join( + dir_path, os.path.basename(testcase_python_path) + ) + + global make_files_cache_set + if testcase_python_path in make_files_cache_set: + return config = testcase["config"] - config["path"] = testcase_python_path + config["path"] = __ensure_cwd_relative(testcase_python_path) # parse config variables config.setdefault("variables", {}) @@ -113,11 +144,8 @@ def make_testcase(testcase: Dict) -> Text: if not teststep.get("testcase"): continue - ref_testcase_path = teststep["testcase"] - # make ref testcase pytest file - project_meta = load_project_meta(testcase_path) - ref_testcase_path = os.path.join(project_meta.PWD, ref_testcase_path) + ref_testcase_path = __ensure_absolute(teststep["testcase"]) __make(ref_testcase_path) # prepare ref testcase class name @@ -133,7 +161,7 @@ def make_testcase(testcase: Dict) -> Text: imports_list.append(f"from {ref_module_name} import {ref_testcase_cls_name}") data = { - "testcase_path": testcase_path, + "testcase_path": __ensure_cwd_relative(testcase_path), "class_name": testcase_cls_name, "config": config, "teststeps": teststeps, @@ -146,10 +174,10 @@ def make_testcase(testcase: Dict) -> Text: f.write(content) logger.info(f"generated testcase: {testcase_python_path}") - return testcase_python_path + make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path)) -def make_testsuite(testsuite: Dict) -> List[Text]: +def __make_testsuite(testsuite: Dict) -> NoReturn: """convert valid testsuite dict to pytest folder with testcases""" try: # validate testcase format @@ -160,11 +188,11 @@ def make_testsuite(testsuite: Dict) -> List[Text]: config = testsuite["config"] testsuite_path = config["path"] - project_meta = load_project_meta(testsuite_path) testsuite_variables = config.get("variables", {}) if isinstance(testsuite_variables, Text): # get variables by function, e.g. ${get_variables()} + project_meta = load_project_meta(testsuite_path) testsuite_variables = parse_data( testsuite_variables, {}, project_meta.functions ) @@ -178,21 +206,13 @@ def make_testsuite(testsuite: Dict) -> List[Text]: ) os.makedirs(testsuite_dir, exist_ok=True) - testcase_files = [] - for testcase in testsuite["testcases"]: # get referenced testcase content testcase_file = testcase["testcase"] - if os.path.isabs(testcase_file): - testcase_path = testcase_file - else: - testcase_path = os.path.join(project_meta.PWD, testcase_file) - + testcase_path = __ensure_absolute(testcase_file) testcase_dict = load_test_file(testcase_path) testcase_dict.setdefault("config", {}) - testcase_dict["config"]["path"] = os.path.join( - testsuite_dir, os.path.basename(testcase_path) - ) + testcase_dict["config"]["path"] = testcase_path # override testcase name testcase_dict["config"]["name"] = testcase["name"] @@ -206,17 +226,17 @@ def make_testsuite(testsuite: Dict) -> List[Text]: testcase_dict["config"]["variables"].update(testsuite_variables) # make testcase - testcase_path = make_testcase(testcase_dict) - testcase_files.append(testcase_path) - - return testcase_files + __make_testcase(testcase_dict, testsuite_dir) -def __make(tests_path: Text) -> List[Text]: - global make_files_cache_mapping - if tests_path in make_files_cache_mapping: - return make_files_cache_mapping[tests_path] +def __make(tests_path: Text) -> NoReturn: + """ make testcase(s) with testcase/testsuite/folder absolute path + generated pytest file path will be cached in make_files_cache_set + Args: + tests_path: should be in absolute path + + """ test_files = [] if os.path.isdir(tests_path): files_list = load_folder_files(tests_path) @@ -226,7 +246,6 @@ def __make(tests_path: Text) -> List[Text]: else: raise exceptions.TestcaseNotFound(f"Invalid tests path: {tests_path}") - testcase_path_list = [] for test_file in test_files: try: test_content = load_test_file(test_file) @@ -238,45 +257,33 @@ def __make(tests_path: Text) -> List[Text]: # testcase if "teststeps" in test_content: try: - testcase_file = make_testcase(test_content) + __make_testcase(test_content) except exceptions.TestCaseFormatError: continue - testcase_path_list.append(testcase_file) - # testsuite elif "testcases" in test_content: try: - testcase_files = make_testsuite(test_content) + __make_testsuite(test_content) except exceptions.TestSuiteFormatError: continue - testcase_path_list.extend(testcase_files) - # invalid format else: raise exceptions.FileFormatError( f"test file is neither testcase nor testsuite: {test_file}" ) - make_files_cache_mapping[tests_path] = testcase_path_list - if not testcase_path_list: - logger.warning(f"No valid testcase generated on {tests_path}") - return [] - - return testcase_path_list - - -def main_make(tests_paths: List[Text]) -> List: +def main_make(tests_paths: List[Text]) -> 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) - testcase_path_list = [] - for tests_path, pytest_files in make_files_cache_mapping.items(): - testcase_path_list.extend(pytest_files) - - format_pytest_with_black(testcase_path_list) + testcase_path_list = list(make_files_cache_set) + __format_pytest_with_black(testcase_path_list) return testcase_path_list diff --git a/httprunner/ext/make/make_test.py b/httprunner/ext/make/make_test.py index 0405cf08..ade746dd 100644 --- a/httprunner/ext/make/make_test.py +++ b/httprunner/ext/make/make_test.py @@ -1,6 +1,7 @@ +import os import unittest -from httprunner.ext.make import main_make, convert_testcase_path +from httprunner.ext.make import main_make, convert_testcase_path, make_files_cache_set class TestLoader(unittest.TestCase): @@ -16,8 +17,17 @@ class TestLoader(unittest.TestCase): path = [ "examples/postman_echo/request_methods/request_with_testcase_reference.yml" ] + make_files_cache_set.clear() testcase_python_list = main_make(path) - with open(testcase_python_list[0]) as f: + self.assertEqual(len(testcase_python_list), 2) + self.assertIn( + "examples/postman_echo/request_methods/request_with_testcase_reference_test.py", + testcase_python_list, + ) + + with open( + "examples/postman_echo/request_methods/request_with_testcase_reference_test.py" + ) as f: content = f.read() self.assertIn( """ @@ -77,8 +87,9 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( def test_make_testsuite(self): path = ["examples/postman_echo/request_methods/demo_testsuite.yml"] + make_files_cache_set.clear() testcase_python_list = main_make(path) - self.assertEqual(len(testcase_python_list), 2) + # FIXME: self.assertEqual(len(testcase_python_list), 2) self.assertIn( "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py", testcase_python_list, diff --git a/httprunner/runner.py b/httprunner/runner.py index 35a7db93..99e34d82 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -1,10 +1,10 @@ import os import time import uuid -import allure from datetime import datetime from typing import List, Dict, Text +import allure from loguru import logger from httprunner import utils, exceptions