From 528ddd0c67ea0dbcb0bcf244344d62e93f78a2d7 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 10:35:09 +0800 Subject: [PATCH 01/30] refactor: rename schema to models --- examples/httpbin/basic_test.py | 2 +- examples/httpbin/hooks_test.py | 2 +- examples/httpbin/load_image_test.py | 2 +- examples/httpbin/upload_test.py | 2 +- examples/httpbin/validate_test.py | 2 +- .../postman_echo/cookie_manipulation/set_delete_cookies.py | 2 +- .../demo_testsuite_yml/request_with_functions_test.py | 2 +- .../request_with_testcase_reference_test.py | 2 +- examples/postman_echo/request_methods/hardcode_test.py | 2 +- .../request_methods/request_with_functions_test.py | 2 +- .../request_methods/request_with_testcase_reference_test.py | 2 +- .../request_methods/request_with_variables_test.py | 2 +- .../request_methods/validate_with_functions_test.py | 2 +- .../request_methods/validate_with_variables_test.py | 2 +- httprunner/__init__.py | 2 +- httprunner/app/routers/debug.py | 2 +- httprunner/client.py | 4 ++-- httprunner/ext/uploader/__init__.py | 2 +- httprunner/loader.py | 2 +- httprunner/{schema.py => models.py} | 0 httprunner/parser.py | 2 +- httprunner/response.py | 2 +- httprunner/runner.py | 2 +- httprunner/testcase.py | 2 +- pyproject.toml | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) rename httprunner/{schema.py => models.py} (100%) diff --git a/examples/httpbin/basic_test.py b/examples/httpbin/basic_test.py index 24cd405e..b74d7c1d 100644 --- a/examples/httpbin/basic_test.py +++ b/examples/httpbin/basic_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/basic.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/hooks_test.py b/examples/httpbin/hooks_test.py index 0e2cd0b9..aeb92dd3 100644 --- a/examples/httpbin/hooks_test.py +++ b/examples/httpbin/hooks_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/hooks.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/load_image_test.py b/examples/httpbin/load_image_test.py index 36453b6c..16a9044c 100644 --- a/examples/httpbin/load_image_test.py +++ b/examples/httpbin/load_image_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/load_image.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/upload_test.py b/examples/httpbin/upload_test.py index 53ce4461..d89d30f9 100644 --- a/examples/httpbin/upload_test.py +++ b/examples/httpbin/upload_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/upload.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/validate_test.py b/examples/httpbin/validate_test.py index c9115e60..8e7d8618 100644 --- a/examples/httpbin/validate_test.py +++ b/examples/httpbin/validate_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/validate.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/cookie_manipulation/set_delete_cookies.py b/examples/postman_echo/cookie_manipulation/set_delete_cookies.py index b2e3bf26..43ddabfb 100644 --- a/examples/postman_echo/cookie_manipulation/set_delete_cookies.py +++ b/examples/postman_echo/cookie_manipulation/set_delete_cookies.py @@ -2,7 +2,7 @@ import unittest import requests from httprunner.runner import HttpRunner -from httprunner.schema import TConfig, TStep +from httprunner.models import TConfig, TStep class TestCaseSetDeleteCookies(unittest.TestCase): 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 345887c2..474e99c2 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,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase 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 ca0232b9..09e9f7c2 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,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os diff --git a/examples/postman_echo/request_methods/hardcode_test.py b/examples/postman_echo/request_methods/hardcode_test.py index 09fa0cfd..a499659b 100644 --- a/examples/postman_echo/request_methods/hardcode_test.py +++ b/examples/postman_echo/request_methods/hardcode_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/hardcode.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_functions_test.py b/examples/postman_echo/request_methods/request_with_functions_test.py index 99a103e9..340749f5 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py index 24a5d107..837dff4d 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os diff --git a/examples/postman_echo/request_methods/request_with_variables_test.py b/examples/postman_echo/request_methods/request_with_variables_test.py index ecfa1586..47eff628 100644 --- a/examples/postman_echo/request_methods/request_with_variables_test.py +++ b/examples/postman_echo/request_methods/request_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/validate_with_functions_test.py b/examples/postman_echo/request_methods/validate_with_functions_test.py index 4378c92b..b077ebc8 100644 --- a/examples/postman_echo/request_methods/validate_with_functions_test.py +++ b/examples/postman_echo/request_methods/validate_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/validate_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/validate_with_variables_test.py b/examples/postman_echo/request_methods/validate_with_variables_test.py index 3c3c0291..c333f462 100644 --- a/examples/postman_echo/request_methods/validate_with_variables_test.py +++ b/examples/postman_echo/request_methods/validate_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.8 +# NOTICE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/validate_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/httprunner/__init__.py b/httprunner/__init__.py index 5f9324f6..40f1a9c5 100644 --- a/httprunner/__init__.py +++ b/httprunner/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.0.8" +__version__ = "3.0.9" __description__ = "One-stop solution for HTTP(S) testing." from httprunner.runner import HttpRunner diff --git a/httprunner/app/routers/debug.py b/httprunner/app/routers/debug.py index 9d99f04c..192fbc6e 100644 --- a/httprunner/app/routers/debug.py +++ b/httprunner/app/routers/debug.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from httprunner.runner import HttpRunner -from httprunner.schema import ProjectMeta, TestCase +from httprunner.models import ProjectMeta, TestCase router = APIRouter() runner = HttpRunner() diff --git a/httprunner/client.py b/httprunner/client.py index 01f10e0a..6bba32a3 100644 --- a/httprunner/client.py +++ b/httprunner/client.py @@ -13,8 +13,8 @@ from requests.exceptions import ( ) from sentry_sdk import capture_exception -from httprunner.schema import RequestData, ResponseData -from httprunner.schema import SessionData, ReqRespData +from httprunner.models import RequestData, ResponseData +from httprunner.models import SessionData, ReqRespData from httprunner.utils import lower_dict_keys, omit_long_data urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) diff --git a/httprunner/ext/uploader/__init__.py b/httprunner/ext/uploader/__init__.py index 0d177f65..8768f8b2 100644 --- a/httprunner/ext/uploader/__init__.py +++ b/httprunner/ext/uploader/__init__.py @@ -49,7 +49,7 @@ from typing import Text, NoReturn from loguru import logger from httprunner.parser import parse_variables_mapping -from httprunner.schema import TStep, FunctionsMapping +from httprunner.models import TStep, FunctionsMapping try: import filetype diff --git a/httprunner/loader.py b/httprunner/loader.py index 821c9204..880d5918 100644 --- a/httprunner/loader.py +++ b/httprunner/loader.py @@ -13,7 +13,7 @@ from pydantic import ValidationError from httprunner import builtin, utils from httprunner import exceptions -from httprunner.schema import TestCase, ProjectMeta, TestSuite +from httprunner.models import TestCase, ProjectMeta, TestSuite try: # PyYAML version >= 5.1 diff --git a/httprunner/schema.py b/httprunner/models.py similarity index 100% rename from httprunner/schema.py rename to httprunner/models.py diff --git a/httprunner/parser.py b/httprunner/parser.py index c36363ee..74f4f69e 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -6,7 +6,7 @@ from typing import Any, Set, Text, Callable, List, Dict from sentry_sdk import capture_exception from httprunner import loader, utils, exceptions -from httprunner.schema import VariablesMapping, FunctionsMapping +from httprunner.models import VariablesMapping, FunctionsMapping absolute_http_url_regexp = re.compile(r"^https?://", re.I) diff --git a/httprunner/response.py b/httprunner/response.py index 9a62c708..41171271 100644 --- a/httprunner/response.py +++ b/httprunner/response.py @@ -6,7 +6,7 @@ from loguru import logger from httprunner.exceptions import ValidationFailure, ParamsError from httprunner.parser import parse_data, parse_string_value, get_mapping_function -from httprunner.schema import VariablesMapping, Validators, FunctionsMapping +from httprunner.models import VariablesMapping, Validators, FunctionsMapping def get_uniform_comparator(comparator: Text): diff --git a/httprunner/runner.py b/httprunner/runner.py index b90886ea..4a3e77be 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -21,7 +21,7 @@ from httprunner.loader import load_project_meta, load_testcase_file from httprunner.parser import build_url, parse_data, parse_variables_mapping from httprunner.response import ResponseObject from httprunner.testcase import Config, Step -from httprunner.schema import ( +from httprunner.models import ( TConfig, TStep, VariablesMapping, diff --git a/httprunner/testcase.py b/httprunner/testcase.py index fc738851..3d9f8c3a 100644 --- a/httprunner/testcase.py +++ b/httprunner/testcase.py @@ -1,7 +1,7 @@ import inspect from typing import Text, Any, Union, Callable -from httprunner.schema import ( +from httprunner.models import ( TConfig, TStep, TRequest, diff --git a/pyproject.toml b/pyproject.toml index 2b239b6a..23324ab6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "httprunner" -version = "3.0.8" +version = "3.0.9" description = "One-stop solution for HTTP(S) testing." license = "Apache-2.0" readme = "README.md" From adf0967888ed9581a2cd137952e964513c409a5e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 11:31:51 +0800 Subject: [PATCH 02/30] refactor: use step export to export session variables from referenced testcase --- .../request_with_testcase_reference.yml | 2 +- .../request_with_testcase_reference_test.py | 2 +- httprunner/compat.py | 12 ++++----- httprunner/make.py | 23 ++++++++--------- httprunner/models.py | 5 +++- httprunner/runner.py | 25 +++++++++++-------- httprunner/testcase.py | 5 ++-- 7 files changed, 39 insertions(+), 35 deletions(-) diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference.yml b/examples/postman_echo/request_methods/request_with_testcase_reference.yml index 85138047..37871f2c 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference.yml +++ b/examples/postman_echo/request_methods/request_with_testcase_reference.yml @@ -11,7 +11,7 @@ teststeps: variables: foo1: override_bar1 testcase: request_methods/request_with_functions.yml - extract: + export: - session_foo2 - name: post form data diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py index 837dff4d..75c211ff 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py @@ -26,7 +26,7 @@ class TestCaseRequestWithTestcaseReference(HttpRunner): RunTestCase("request with functions") .with_variables(**{"foo1": "override_bar1"}) .call(RequestWithFunctions) - .extract(*["session_foo2"]) + .export(*["session_foo2"]) ), Step( RunRequest("post form data") diff --git a/httprunner/compat.py b/httprunner/compat.py index 54addff2..8f919300 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -133,10 +133,10 @@ def ensure_step_attachment(step: Dict) -> Dict: test_dict["teardown_hooks"] = step["teardown_hooks"] if "extract" in step: - if step.get("request"): - test_dict["extract"] = convert_extractors(step["extract"]) - elif step.get("testcase"): - test_dict["extract"] = step["extract"] + test_dict["extract"] = convert_extractors(step["extract"]) + + if "export" in step: + test_dict["export"] = step["export"] if "validate" in step: test_dict["validate"] = convert_validators(step["validate"]) @@ -167,8 +167,6 @@ def ensure_testcase_v3(test_content: Dict) -> Dict: for step in test_content["teststeps"]: teststep = {} - teststep.update(ensure_step_attachment(step)) - if "request" in step: teststep["request"] = step.pop("request") elif "api" in step: @@ -176,6 +174,8 @@ def ensure_testcase_v3(test_content: Dict) -> Dict: elif "testcase" in step: teststep["testcase"] = step.pop("testcase") + teststep.update(ensure_step_attachment(step)) + teststep = sort_step_by_custom_order(teststep) v3_content["teststeps"].append(teststep) diff --git a/httprunner/make.py b/httprunner/make.py index 66fd9a1b..5a1dcfc0 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -220,18 +220,16 @@ def make_teststep_chain_style(teststep: Dict) -> Text: call_ref_testcase = f".call({testcase})" step_info += call_ref_testcase - extract_info = teststep.get("extract") - if extract_info: - if isinstance(extract_info, Dict): - # request step - step_info += ".extract()" - for extract_name, extract_path in extract_info.items(): - step_info += f'.with_jmespath("{extract_path}", "{extract_name}")' - elif isinstance(extract_info, List): - # reference testcase step - step_info += f".extract(*{extract_info})" - else: - raise exceptions.TestCaseFormatError(f"Invalid extract: {extract_info}") + if "extract" in teststep: + # request step + step_info += ".extract()" + for extract_name, extract_path in teststep["extract"].items(): + step_info += f'.with_jmespath("{extract_path}", "{extract_name}")' + + if "export" in teststep: + # reference testcase step + export: List[Text] = teststep["export"] + step_info += f".export(*{export})" if "validate" in teststep: step_info += ".validate()" @@ -455,6 +453,7 @@ def main_make(tests_paths: List[Text]) -> List[Text]: pytest_files_set.update(make_files_cache_set) pytest_files_list = list(pytest_files_set) + # TODO: format referenced testcase format_pytest_with_black(*pytest_files_list) return pytest_files_list diff --git a/httprunner/models.py b/httprunner/models.py index be60bf41..8389a886 100644 --- a/httprunner/models.py +++ b/httprunner/models.py @@ -66,7 +66,10 @@ class TStep(BaseModel): variables: VariablesMapping = {} setup_hooks: Hook = [] teardown_hooks: Hook = [] - extract: Union[Dict[Text, Text], List[Text]] = {} + # used to extract request's response field + extract: Dict[Text, Text] = {} + # used to export session variables from referenced testcase + export: List[Text] = [] validators: Validators = Field([], alias="validate") validate_script: List[Text] = [] diff --git a/httprunner/runner.py b/httprunner/runner.py index 4a3e77be..af280a9c 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -43,10 +43,10 @@ class HttpRunner(object): __teststeps: List[TStep] __project_meta: ProjectMeta = None __case_id: Text = "" + __export: List[Text] = [] __step_datas: List[StepData] = None __session: HttpSession = None __session_variables: VariablesMapping = {} - __export_variables: VariablesMapping = {} # time __start_at: float = 0 __duration: float = 0 @@ -82,6 +82,10 @@ class HttpRunner(object): self.__session_variables = variables return self + def with_export(self, export: List[Text]) -> "HttpRunner": + self.__export = export + return self + def __run_step_request(self, step: TStep) -> StepData: """run teststep: request""" step_data = StepData(name=step.name) @@ -166,6 +170,7 @@ class HttpRunner(object): """run teststep: referenced testcase""" step_data = StepData(name=step.name) step_variables = step.variables + step_export = step.export if hasattr(step.testcase, "config") and hasattr(step.testcase, "teststeps"): testcase_cls = step.testcase @@ -174,6 +179,7 @@ class HttpRunner(object): .with_session(self.__session) .with_case_id(self.__case_id) .with_variables(step_variables) + .with_export(step_export) .run() ) @@ -188,6 +194,7 @@ class HttpRunner(object): .with_session(self.__session) .with_case_id(self.__case_id) .with_variables(step_variables) + .with_export(step_export) .run_path(ref_testcase_path) ) @@ -201,6 +208,9 @@ class HttpRunner(object): step_data.success = case_result.success self.success &= case_result.success + if step_data.export: + logger.info(f"export variables: {step_data.export}") + return step_data def __run_step(self, step: TStep) -> Dict: @@ -252,7 +262,6 @@ class HttpRunner(object): self.__step_datas: List[StepData] = [] self.__session = self.__session or HttpSession() self.__session_variables = {} - self.__export_variables = {} # run teststeps for step in self.__teststeps: @@ -274,11 +283,6 @@ class HttpRunner(object): self.__session_variables.update(extract_mapping) self.__duration = time.time() - self.__start_at - - self.__export_variables = self.get_export_variables() - if self.__export_variables: - logger.info(f"export variables: {self.__export_variables}") - return self def run_path(self, path: Text) -> "HttpRunner": @@ -303,11 +307,10 @@ class HttpRunner(object): return self.__step_datas def get_export_variables(self) -> Dict: - if self.__export_variables: - return self.__export_variables - + # override testcase export vars with step export + export_var_names = self.__export or self.__config.export export_vars_mapping = {} - for var_name in self.__config.export: + for var_name in export_var_names: if var_name not in self.__session_variables: raise ParamsError( f"failed to export variable {var_name} from session variables {self.__session_variables}" diff --git a/httprunner/testcase.py b/httprunner/testcase.py index 3d9f8c3a..30016adc 100644 --- a/httprunner/testcase.py +++ b/httprunner/testcase.py @@ -298,10 +298,9 @@ class RunRequest(object): class StepRefCase(object): def __init__(self, step: TStep): self.__t_step = step - self.__t_step.extract = [] - def extract(self, *var_name: Text) -> "StepRefCase": - self.__t_step.extract.extend(var_name) + def export(self, *var_name: Text) -> "StepRefCase": + self.__t_step.export.extend(var_name) return self def perform(self) -> TStep: From 6ba5f703a4b090463a2ebd1bdabe9ea08b437c7f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 11:34:42 +0800 Subject: [PATCH 03/30] refactor: rename TestCaseInOut field --- httprunner/models.py | 4 ++-- httprunner/runner.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/httprunner/models.py b/httprunner/models.py index 8389a886..e1cb7a37 100644 --- a/httprunner/models.py +++ b/httprunner/models.py @@ -99,8 +99,8 @@ class TestCaseTime(BaseModel): class TestCaseInOut(BaseModel): - vars: VariablesMapping = {} - export: Dict = {} + config_vars: VariablesMapping = {} + export_vars: Dict = {} class RequestStat(BaseModel): diff --git a/httprunner/runner.py b/httprunner/runner.py index af280a9c..a80ba335 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -334,7 +334,7 @@ class HttpRunner(object): duration=self.__duration, ), in_out=TestCaseInOut( - vars=self.__config.variables, export=self.get_export_variables() + config_vars=self.__config.variables, export_vars=self.get_export_variables() ), log=self.__log_path, step_datas=self.__step_datas, From 6c12f531f3ee6d03e22f4abff5c4ea3c1b53c06b Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 11:36:19 +0800 Subject: [PATCH 04/30] refactor: update TStep extract/export type --- httprunner/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httprunner/models.py b/httprunner/models.py index e1cb7a37..99c41091 100644 --- a/httprunner/models.py +++ b/httprunner/models.py @@ -67,9 +67,9 @@ class TStep(BaseModel): setup_hooks: Hook = [] teardown_hooks: Hook = [] # used to extract request's response field - extract: Dict[Text, Text] = {} + extract: VariablesMapping = {} # used to export session variables from referenced testcase - export: List[Text] = [] + export: Export = [] validators: Validators = Field([], alias="validate") validate_script: List[Text] = [] From 86b69638c3904e35cbf207652028da127ad01b95 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 11:39:50 +0800 Subject: [PATCH 05/30] refactor: rename StepData export to export_vars --- httprunner/models.py | 2 +- httprunner/runner.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/httprunner/models.py b/httprunner/models.py index 99c41091..449f168f 100644 --- a/httprunner/models.py +++ b/httprunner/models.py @@ -148,7 +148,7 @@ class StepData(BaseModel): success: bool = False name: Text = "" # teststep name data: Union[SessionData, List[SessionData]] = None - export: Dict = {} + export_vars: VariablesMapping = {} class TestCaseSummary(BaseModel): diff --git a/httprunner/runner.py b/httprunner/runner.py index a80ba335..4489c60b 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -138,7 +138,7 @@ class HttpRunner(object): # extract extractors = step.extract extract_mapping = resp_obj.extract(extractors) - step_data.export = extract_mapping + step_data.export_vars = extract_mapping variables_mapping = step.variables variables_mapping.update(extract_mapping) @@ -204,12 +204,12 @@ class HttpRunner(object): ) step_data.data = case_result.get_step_datas() # list of step data - step_data.export = case_result.get_export_variables() + step_data.export_vars = case_result.get_export_variables() step_data.success = case_result.success self.success &= case_result.success - if step_data.export: - logger.info(f"export variables: {step_data.export}") + if step_data.export_vars: + logger.info(f"export variables: {step_data.export_vars}") return step_data @@ -228,7 +228,7 @@ class HttpRunner(object): self.__step_datas.append(step_data) logger.info(f"run step end: {step.name} <<<<<<\n") - return step_data.export + return step_data.export_vars def __parse_config(self, config: TConfig) -> NoReturn: config.variables.update(self.__session_variables) From c0d8e65e4939b1059a54f3e2804fc3542ac26d41 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 13:26:01 +0800 Subject: [PATCH 06/30] fix: miss formatting referenced testcase --- httprunner/make.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/httprunner/make.py b/httprunner/make.py index 5a1dcfc0..9c0e6878 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -22,6 +22,8 @@ from httprunner.response import uniform_validator """ cache converted pytest files, avoid duplicate making """ make_files_cache_set: Set = set() +""" save generated pytest files to run, except referenced testcase +""" pytest_files_set: Set = set() __TEMPLATE__ = jinja2.Template( @@ -251,9 +253,7 @@ def make_teststep_chain_style(teststep: Dict) -> Text: return f"Step({step_info})" -def make_testcase( - testcase: Dict, dir_path: Text = None, ref_flag: bool = False, -) -> Text: +def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: """convert valid testcase dict to pytest file path""" # ensure compatibility with testcase format v2 testcase = ensure_testcase_v3(testcase) @@ -295,7 +295,9 @@ def make_testcase( # make ref testcase pytest file ref_testcase_path = __ensure_absolute(teststep["testcase"]) - __make(ref_testcase_path, ref_flag=True) + test_content = load_test_file(ref_testcase_path) + test_content.setdefault("config", {})["path"] = ref_testcase_path + make_testcase(test_content) # prepare ref testcase class name ref_testcase_python_path, ref_testcase_cls_name = convert_testcase_path( @@ -330,8 +332,7 @@ def make_testcase( logger.info(f"generated testcase: {testcase_python_path}") - if not ref_flag: - make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path)) + make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path)) return testcase_python_path @@ -384,16 +385,16 @@ def make_testsuite(testsuite: Dict) -> NoReturn: testcase_dict["config"]["variables"].update(testsuite_variables) # make testcase - make_testcase(testcase_dict, testsuite_dir) + testcase_pytest_path = make_testcase(testcase_dict, testsuite_dir) + pytest_files_set.add(testcase_pytest_path) -def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn: +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 - ref_flag: flag if referenced test path """ test_files = [] @@ -408,6 +409,7 @@ def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn: for test_file in test_files: if test_file.lower().endswith("_test.py"): pytest_files_set.add(test_file) + make_files_cache_set.add(test_file) continue try: @@ -425,7 +427,8 @@ 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) + testcase_pytest_path = make_testcase(test_content) + pytest_files_set.add(testcase_pytest_path) except exceptions.TestCaseFormatError: continue @@ -451,11 +454,11 @@ def main_make(tests_paths: List[Text]) -> List[Text]: __make(tests_path) - pytest_files_set.update(make_files_cache_set) - pytest_files_list = list(pytest_files_set) - # TODO: format referenced testcase - format_pytest_with_black(*pytest_files_list) - return pytest_files_list + # format pytest files + make_files_list = list(make_files_cache_set) + format_pytest_with_black(*make_files_list) + + return list(pytest_files_set) def init_make_parser(subparsers): From 2b9a1e574d47497a0b881acc8796465ee1897ee0 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 13:39:21 +0800 Subject: [PATCH 07/30] refactor: remove ref_flag --- .../request_with_testcase_reference_test.py | 2 +- httprunner/make.py | 33 ++++++++-------- tests/make_test.py | 38 +++++++++++++------ 3 files changed, 43 insertions(+), 30 deletions(-) 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 09e9f7c2..35a113d2 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 @@ -26,7 +26,7 @@ class TestCaseRequestWithTestcaseReference(HttpRunner): RunTestCase("request with functions") .with_variables(**{"foo1": "override_bar1"}) .call(RequestWithFunctions) - .extract(*["session_foo2"]) + .export(*["session_foo2"]) ), Step( RunRequest("post form data") diff --git a/httprunner/make.py b/httprunner/make.py index 9c0e6878..c35f6dc4 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -21,10 +21,11 @@ from httprunner.response import uniform_validator """ cache converted pytest files, avoid duplicate making """ -make_files_cache_set: Set = set() +pytest_files_made_cache_mapping: Dict[Text, Text] = {} + """ save generated pytest files to run, except referenced testcase """ -pytest_files_set: Set = set() +pytest_files_run_set: Set = set() __TEMPLATE__ = jinja2.Template( """# NOTICE: Generated By HttpRunner v{{ version }} @@ -270,8 +271,8 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: dir_path, os.path.basename(testcase_python_path) ) - global make_files_cache_set - if testcase_python_path in make_files_cache_set: + global pytest_files_made_cache_mapping + if testcase_python_path in pytest_files_made_cache_mapping: return testcase_python_path config = testcase["config"] @@ -297,12 +298,10 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: ref_testcase_path = __ensure_absolute(teststep["testcase"]) test_content = load_test_file(ref_testcase_path) test_content.setdefault("config", {})["path"] = ref_testcase_path - make_testcase(test_content) + ref_testcase_python_path = make_testcase(test_content) # prepare ref testcase class name - ref_testcase_python_path, ref_testcase_cls_name = convert_testcase_path( - ref_testcase_path - ) + ref_testcase_cls_name = pytest_files_made_cache_mapping[ref_testcase_python_path] teststep["testcase"] = ref_testcase_cls_name # prepare import ref testcase @@ -328,12 +327,11 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: with open(testcase_python_path, "w", encoding="utf-8") as f: f.write(content) + pytest_files_made_cache_mapping[testcase_python_path] = testcase_cls_name __ensure_testcase_module(testcase_python_path) logger.info(f"generated testcase: {testcase_python_path}") - make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path)) - return testcase_python_path @@ -386,12 +384,12 @@ def make_testsuite(testsuite: Dict) -> NoReturn: # make testcase testcase_pytest_path = make_testcase(testcase_dict, testsuite_dir) - pytest_files_set.add(testcase_pytest_path) + pytest_files_run_set.add(testcase_pytest_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 + generated pytest file path will be cached in pytest_files_made_cache_mapping Args: tests_path: should be in absolute path @@ -408,8 +406,7 @@ def __make(tests_path: Text) -> NoReturn: for test_file in test_files: if test_file.lower().endswith("_test.py"): - pytest_files_set.add(test_file) - make_files_cache_set.add(test_file) + pytest_files_run_set.add(test_file) continue try: @@ -428,7 +425,7 @@ def __make(tests_path: Text) -> NoReturn: if "teststeps" in test_content: try: testcase_pytest_path = make_testcase(test_content) - pytest_files_set.add(testcase_pytest_path) + pytest_files_run_set.add(testcase_pytest_path) except exceptions.TestCaseFormatError: continue @@ -455,10 +452,10 @@ def main_make(tests_paths: List[Text]) -> List[Text]: __make(tests_path) # format pytest files - make_files_list = list(make_files_cache_set) - format_pytest_with_black(*make_files_list) + pytest_files_format_list = pytest_files_made_cache_mapping.keys() + format_pytest_with_black(*pytest_files_format_list) - return list(pytest_files_set) + return list(pytest_files_run_set) def init_make_parser(subparsers): diff --git a/tests/make_test.py b/tests/make_test.py index 8eec7ca6..8b0e9e44 100644 --- a/tests/make_test.py +++ b/tests/make_test.py @@ -1,12 +1,13 @@ +import os import unittest from httprunner.make import ( main_make, convert_testcase_path, - make_files_cache_set, + pytest_files_made_cache_mapping, make_config_chain_style, make_teststep_chain_style, - pytest_files_set, + pytest_files_run_set, ) @@ -16,19 +17,25 @@ class TestMake(unittest.TestCase): testcase_python_list = main_make(path) self.assertEqual( testcase_python_list[0], - "examples/postman_echo/request_methods/request_with_variables_test.py", + os.path.join( + os.getcwd(), + "examples/postman_echo/request_methods/request_with_variables_test.py", + ), ) def test_make_testcase_with_ref(self): path = [ "examples/postman_echo/request_methods/request_with_testcase_reference.yml" ] - make_files_cache_set.clear() - pytest_files_set.clear() + pytest_files_made_cache_mapping.clear() + pytest_files_run_set.clear() testcase_python_list = main_make(path) self.assertEqual(len(testcase_python_list), 1) self.assertIn( - "examples/postman_echo/request_methods/request_with_testcase_reference_test.py", + os.path.join( + os.getcwd(), + "examples/postman_echo/request_methods/request_with_testcase_reference_test.py", + ), testcase_python_list, ) @@ -52,7 +59,10 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( path = ["examples/postman_echo/request_methods/"] testcase_python_list = main_make(path) self.assertIn( - "examples/postman_echo/request_methods/request_with_functions_test.py", + os.path.join( + os.getcwd(), + "examples/postman_echo/request_methods/request_with_functions_test.py", + ), testcase_python_list, ) @@ -92,16 +102,22 @@ 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() - pytest_files_set.clear() + pytest_files_made_cache_mapping.clear() + pytest_files_run_set.clear() testcase_python_list = main_make(path) self.assertEqual(len(testcase_python_list), 2) self.assertIn( - "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py", + os.path.join( + os.getcwd(), + "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py", + ), testcase_python_list, ) self.assertIn( - "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py", + os.path.join( + os.getcwd(), + "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py", + ), testcase_python_list, ) From 6b38534ac28c5889716cb486342b718dd127dad0 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 14:38:42 +0800 Subject: [PATCH 08/30] docs: update changelog --- docs/CHANGELOG.md | 12 ++++++++++++ httprunner/make.py | 4 +++- httprunner/runner.py | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c9518eef..045004c7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,17 @@ # Release History +## 3.0.9 (2020-06-05) + +**Fixed** + +- fix: miss formatting referenced testcase + +**Changed** + +- change: add `export` keyword in TStep to export session variables from referenced testcase +- change: rename TestCaseInOut field, config_vars and export_vars +- change: rename StepData field, export_vars + ## 3.0.8 (2020-06-04) **Added** diff --git a/httprunner/make.py b/httprunner/make.py index c35f6dc4..1099a12e 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -301,7 +301,9 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: ref_testcase_python_path = make_testcase(test_content) # prepare ref testcase class name - ref_testcase_cls_name = pytest_files_made_cache_mapping[ref_testcase_python_path] + ref_testcase_cls_name = pytest_files_made_cache_mapping[ + ref_testcase_python_path + ] teststep["testcase"] = ref_testcase_cls_name # prepare import ref testcase diff --git a/httprunner/runner.py b/httprunner/runner.py index 4489c60b..d135a8f7 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -334,7 +334,8 @@ class HttpRunner(object): duration=self.__duration, ), in_out=TestCaseInOut( - config_vars=self.__config.variables, export_vars=self.get_export_variables() + config_vars=self.__config.variables, + export_vars=self.get_export_variables(), ), log=self.__log_path, step_datas=self.__step_datas, From e95f9503653180c3528d8fe4f515108aaa3c9b31 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 14:52:28 +0800 Subject: [PATCH 09/30] change: add --tb=short for hrun command --- docs/CHANGELOG.md | 1 + httprunner/cli.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 045004c7..8af01f1f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,7 @@ - change: add `export` keyword in TStep to export session variables from referenced testcase - change: rename TestCaseInOut field, config_vars and export_vars - change: rename StepData field, export_vars +- change: add `--tb=short` for `hrun` command to use shorter traceback format by default ## 3.0.8 (2020-06-04) diff --git a/httprunner/cli.py b/httprunner/cli.py index 0e2b7ae2..7786f43e 100644 --- a/httprunner/cli.py +++ b/httprunner/cli.py @@ -48,6 +48,9 @@ def main_run(extra_args): logger.error("No valid testcases found, exit 1.") sys.exit(1) + if "--tb=short" not in extra_args_new: + extra_args_new.append("--tb=short") + extra_args_new.extend(testcase_path_list) sys.exit(pytest.main(extra_args_new)) From 1fb847d23e53656951e132389f0d6f8fe421904d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 15:47:45 +0800 Subject: [PATCH 10/30] refactor: rename variable step_context --- httprunner/testcase.py | 160 +++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 70 deletions(-) diff --git a/httprunner/testcase.py b/httprunner/testcase.py index 30016adc..91601df9 100644 --- a/httprunner/testcase.py +++ b/httprunner/testcase.py @@ -57,37 +57,43 @@ class Config(object): class StepRequestValidation(object): - def __init__(self, step: TStep): - self.__t_step = step + def __init__(self, step_context: TStep): + self.__step_context = step_context def assert_equal( self, jmes_path: Text, expected_value: Any ) -> "StepRequestValidation": - self.__t_step.validators.append({"equal": [jmes_path, expected_value]}) + self.__step_context.validators.append({"equal": [jmes_path, expected_value]}) return self def assert_not_equal( self, jmes_path: Text, expected_value: Any ) -> "StepRequestValidation": - self.__t_step.validators.append({"not_equal": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"not_equal": [jmes_path, expected_value]} + ) return self def assert_greater_than( self, jmes_path: Text, expected_value: Union[int, float] ) -> "StepRequestValidation": - self.__t_step.validators.append({"greater_than": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"greater_than": [jmes_path, expected_value]} + ) return self def assert_less_than( self, jmes_path: Text, expected_value: Union[int, float] ) -> "StepRequestValidation": - self.__t_step.validators.append({"less_than": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"less_than": [jmes_path, expected_value]} + ) return self def assert_greater_or_equals( self, jmes_path: Text, expected_value: Union[int, float] ) -> "StepRequestValidation": - self.__t_step.validators.append( + self.__step_context.validators.append( {"greater_or_equals": [jmes_path, expected_value]} ) return self @@ -95,19 +101,23 @@ class StepRequestValidation(object): def assert_less_or_equals( self, jmes_path: Text, expected_value: Union[int, float] ) -> "StepRequestValidation": - self.__t_step.validators.append({"less_or_equals": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"less_or_equals": [jmes_path, expected_value]} + ) return self def assert_length_equal( self, jmes_path: Text, expected_value: int ) -> "StepRequestValidation": - self.__t_step.validators.append({"length_equal": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"length_equal": [jmes_path, expected_value]} + ) return self def assert_length_greater_than( self, jmes_path: Text, expected_value: int ) -> "StepRequestValidation": - self.__t_step.validators.append( + self.__step_context.validators.append( {"length_greater_than": [jmes_path, expected_value]} ) return self @@ -115,7 +125,7 @@ class StepRequestValidation(object): def assert_length_less_than( self, jmes_path: Text, expected_value: int ) -> "StepRequestValidation": - self.__t_step.validators.append( + self.__step_context.validators.append( {"length_less_than": [jmes_path, expected_value]} ) return self @@ -123,7 +133,7 @@ class StepRequestValidation(object): def assert_length_greater_or_equals( self, jmes_path: Text, expected_value: int ) -> "StepRequestValidation": - self.__t_step.validators.append( + self.__step_context.validators.append( {"length_greater_or_equals": [jmes_path, expected_value]} ) return self @@ -131,7 +141,7 @@ class StepRequestValidation(object): def assert_length_less_or_equals( self, jmes_path: Text, expected_value: int ) -> "StepRequestValidation": - self.__t_step.validators.append( + self.__step_context.validators.append( {"length_less_or_equals": [jmes_path, expected_value]} ) return self @@ -139,55 +149,65 @@ class StepRequestValidation(object): def assert_string_equals( self, jmes_path: Text, expected_value: int ) -> "StepRequestValidation": - self.__t_step.validators.append({"string_equals": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"string_equals": [jmes_path, expected_value]} + ) return self def assert_startswith( self, jmes_path: Text, expected_value: Text ) -> "StepRequestValidation": - self.__t_step.validators.append({"startswith": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"startswith": [jmes_path, expected_value]} + ) return self def assert_endswith( self, jmes_path: Text, expected_value: Text ) -> "StepRequestValidation": - self.__t_step.validators.append({"endswith": [jmes_path, expected_value]}) + self.__step_context.validators.append({"endswith": [jmes_path, expected_value]}) return self def assert_regex_match( self, jmes_path: Text, expected_value: Text ) -> "StepRequestValidation": - self.__t_step.validators.append({"regex_match": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"regex_match": [jmes_path, expected_value]} + ) return self def assert_contains( self, jmes_path: Text, expected_value: Any ) -> "StepRequestValidation": - self.__t_step.validators.append({"contains": [jmes_path, expected_value]}) + self.__step_context.validators.append({"contains": [jmes_path, expected_value]}) return self def assert_contained_by( self, jmes_path: Text, expected_value: Any ) -> "StepRequestValidation": - self.__t_step.validators.append({"contained_by": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"contained_by": [jmes_path, expected_value]} + ) return self def assert_type_match( self, jmes_path: Text, expected_value: Text ) -> "StepRequestValidation": - self.__t_step.validators.append({"type_match": [jmes_path, expected_value]}) + self.__step_context.validators.append( + {"type_match": [jmes_path, expected_value]} + ) return self def perform(self) -> TStep: - return self.__t_step + return self.__step_context class StepRequestExtraction(object): - def __init__(self, step: TStep): - self.__t_step = step + def __init__(self, step_context: TStep): + self.__step_context = step_context def with_jmespath(self, jmes_path: Text, var_name: Text) -> "StepRequestExtraction": - self.__t_step.extract[var_name] = jmes_path + self.__step_context.extract[var_name] = jmes_path return self # def with_regex(self): @@ -199,134 +219,134 @@ class StepRequestExtraction(object): # pass def validate(self) -> StepRequestValidation: - return StepRequestValidation(self.__t_step) + return StepRequestValidation(self.__step_context) def perform(self) -> TStep: - return self.__t_step + return self.__step_context class RequestWithOptionalArgs(object): - def __init__(self, step: TStep): - self.__t_step = step + def __init__(self, step_context: TStep): + self.__step_context = step_context def with_params(self, **params) -> "RequestWithOptionalArgs": - self.__t_step.request.params.update(params) + self.__step_context.request.params.update(params) return self def with_headers(self, **headers) -> "RequestWithOptionalArgs": - self.__t_step.request.headers.update(headers) + self.__step_context.request.headers.update(headers) return self def with_cookies(self, **cookies) -> "RequestWithOptionalArgs": - self.__t_step.request.cookies.update(cookies) + self.__step_context.request.cookies.update(cookies) return self def with_data(self, data) -> "RequestWithOptionalArgs": - self.__t_step.request.data = data + self.__step_context.request.data = data return self def with_json(self, req_json) -> "RequestWithOptionalArgs": - self.__t_step.request.req_json = req_json + self.__step_context.request.req_json = req_json return self def set_timeout(self, timeout: float) -> "RequestWithOptionalArgs": - self.__t_step.request.timeout = timeout + self.__step_context.request.timeout = timeout return self def set_verify(self, verify: bool) -> "RequestWithOptionalArgs": - self.__t_step.request.verify = verify + self.__step_context.request.verify = verify return self def set_allow_redirects(self, allow_redirects: bool) -> "RequestWithOptionalArgs": - self.__t_step.request.allow_redirects = allow_redirects + self.__step_context.request.allow_redirects = allow_redirects return self def upload(self, **file_info) -> "RequestWithOptionalArgs": - self.__t_step.request.upload.update(file_info) + self.__step_context.request.upload.update(file_info) return self # def hooks(self): # pass def extract(self) -> StepRequestExtraction: - return StepRequestExtraction(self.__t_step) + return StepRequestExtraction(self.__step_context) def validate(self) -> StepRequestValidation: - return StepRequestValidation(self.__t_step) + return StepRequestValidation(self.__step_context) def perform(self) -> TStep: - return self.__t_step + return self.__step_context class RunRequest(object): def __init__(self, name: Text): - self.__t_step = TStep(name=name) + self.__step_context = TStep(name=name) def with_variables(self, **variables) -> "RunRequest": - self.__t_step.variables.update(variables) + self.__step_context.variables.update(variables) return self def get(self, url: Text) -> RequestWithOptionalArgs: - self.__t_step.request = TRequest(method=MethodEnum.GET, url=url) - return RequestWithOptionalArgs(self.__t_step) + self.__step_context.request = TRequest(method=MethodEnum.GET, url=url) + return RequestWithOptionalArgs(self.__step_context) def post(self, url: Text) -> RequestWithOptionalArgs: - self.__t_step.request = TRequest(method=MethodEnum.POST, url=url) - return RequestWithOptionalArgs(self.__t_step) + self.__step_context.request = TRequest(method=MethodEnum.POST, url=url) + return RequestWithOptionalArgs(self.__step_context) def put(self, url: Text) -> RequestWithOptionalArgs: - self.__t_step.request = TRequest(method=MethodEnum.PUT, url=url) - return RequestWithOptionalArgs(self.__t_step) + self.__step_context.request = TRequest(method=MethodEnum.PUT, url=url) + return RequestWithOptionalArgs(self.__step_context) def head(self, url: Text) -> RequestWithOptionalArgs: - self.__t_step.request = TRequest(method=MethodEnum.HEAD, url=url) - return RequestWithOptionalArgs(self.__t_step) + self.__step_context.request = TRequest(method=MethodEnum.HEAD, url=url) + return RequestWithOptionalArgs(self.__step_context) def delete(self, url: Text) -> RequestWithOptionalArgs: - self.__t_step.request = TRequest(method=MethodEnum.DELETE, url=url) - return RequestWithOptionalArgs(self.__t_step) + self.__step_context.request = TRequest(method=MethodEnum.DELETE, url=url) + return RequestWithOptionalArgs(self.__step_context) def options(self, url: Text) -> RequestWithOptionalArgs: - self.__t_step.request = TRequest(method=MethodEnum.OPTIONS, url=url) - return RequestWithOptionalArgs(self.__t_step) + self.__step_context.request = TRequest(method=MethodEnum.OPTIONS, url=url) + return RequestWithOptionalArgs(self.__step_context) def patch(self, url: Text) -> RequestWithOptionalArgs: - self.__t_step.request = TRequest(method=MethodEnum.PATCH, url=url) - return RequestWithOptionalArgs(self.__t_step) + self.__step_context.request = TRequest(method=MethodEnum.PATCH, url=url) + return RequestWithOptionalArgs(self.__step_context) class StepRefCase(object): - def __init__(self, step: TStep): - self.__t_step = step + def __init__(self, step_context: TStep): + self.__step_context = step_context def export(self, *var_name: Text) -> "StepRefCase": - self.__t_step.export.extend(var_name) + self.__step_context.export.extend(var_name) return self def perform(self) -> TStep: - return self.__t_step + return self.__step_context class RunTestCase(object): def __init__(self, name: Text): - self.__t_step = TStep(name=name) + self.__step_context = TStep(name=name) def with_variables(self, **variables) -> "RunTestCase": - self.__t_step.variables.update(variables) + self.__step_context.variables.update(variables) return self def call(self, testcase: Callable) -> StepRefCase: - self.__t_step.testcase = testcase - return StepRefCase(self.__t_step) + self.__step_context.testcase = testcase + return StepRefCase(self.__step_context) def perform(self) -> TStep: - return self.__t_step + return self.__step_context class Step(object): def __init__( self, - step: Union[ + step_context: Union[ StepRequestValidation, StepRequestExtraction, RequestWithOptionalArgs, @@ -334,15 +354,15 @@ class Step(object): StepRefCase, ], ): - self.__t_step = step.perform() + self.__step_context = step_context.perform() @property def request(self) -> TRequest: - return self.__t_step.request + return self.__step_context.request @property def testcase(self) -> TestCase: - return self.__t_step.testcase + return self.__step_context.testcase def perform(self) -> TStep: - return self.__t_step + return self.__step_context From ec21585e00491d9523d494ebbeb9f9b07aa1dade Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 16:21:38 +0800 Subject: [PATCH 11/30] fix: check if valid v2/v3 format --- httprunner/make.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/httprunner/make.py b/httprunner/make.py index 1099a12e..914aad29 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -421,6 +421,9 @@ def __make(tests_path: Text) -> NoReturn: if "request" in test_content: test_content = ensure_testcase_v3_api(test_content) + if not (isinstance(test_content, Dict) and "config" in test_content): + raise exceptions.FileFormatError("Invalid testcase/testsuite v2/v3 format!") + test_content.setdefault("config", {})["path"] = test_file # testcase From 4169aa502c29199c04f69981a52e3f6193fe0839 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 16:24:21 +0800 Subject: [PATCH 12/30] change: do not capture exception for json loads --- httprunner/client.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/httprunner/client.py b/httprunner/client.py index 6bba32a3..1580fecf 100644 --- a/httprunner/client.py +++ b/httprunner/client.py @@ -11,7 +11,6 @@ from requests.exceptions import ( MissingSchema, RequestException, ) -from sentry_sdk import capture_exception from httprunner.models import RequestData, ResponseData from httprunner.models import SessionData, ReqRespData @@ -51,12 +50,12 @@ def get_req_resp_record(resp_obj: Response) -> ReqRespData: except json.JSONDecodeError: # str: a=1&b=2 pass - except UnicodeDecodeError as ex: + except UnicodeDecodeError: # bytes/bytearray: request body in protobuf - capture_exception(ex) - except TypeError as ex: + pass + except TypeError: # neither str nor bytes/bytearray, e.g. - capture_exception(ex) + pass request_content_type = lower_dict_keys(request_headers).get("content-type") if request_content_type and "multipart/form-data" in request_content_type: From 639233a33d8532d7e5422da78b557046e89ac410 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 16:38:40 +0800 Subject: [PATCH 13/30] fix: raise TestCaseFormatError if teststep invalid --- httprunner/compat.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/httprunner/compat.py b/httprunner/compat.py index 8f919300..62eab0b4 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -7,6 +7,7 @@ from typing import List, Dict, Text, Union from loguru import logger +from httprunner import exceptions from httprunner.loader import load_project_meta from httprunner.utils import sort_dict_by_custom_order @@ -173,6 +174,8 @@ def ensure_testcase_v3(test_content: Dict) -> Dict: teststep["testcase"] = step.pop("api") elif "testcase" in step: teststep["testcase"] = step.pop("testcase") + else: + raise exceptions.TestCaseFormatError(f"Invalid teststep: {step}") teststep.update(ensure_step_attachment(step)) From 90c67bb5469624e00619d8166a79ca5ff0a11fbf Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 16:44:42 +0800 Subject: [PATCH 14/30] docs: add comment --- httprunner/compat.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/httprunner/compat.py b/httprunner/compat.py index 62eab0b4..f907c113 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -54,10 +54,12 @@ def convert_extractors(extractors: Union[List, Dict]) -> Dict: v3_extractors: Dict = {} if isinstance(extractors, List): + # [{"varA": "content.varA"}, {"varB": "json.varB"}] for extractor in extractors: for k, v in extractor.items(): v3_extractors[k] = v elif isinstance(extractors, Dict): + # {"varA": "body.varA", "varB": "body.varB"} v3_extractors = extractors else: logger.error(f"Invalid extractor: {extractors}") From 302a7ef13938d787a0ec4dceecac90ef4e9949bf Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 17:04:37 +0800 Subject: [PATCH 15/30] fix: check if valid testcase file path --- httprunner/make.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/httprunner/make.py b/httprunner/make.py index 914aad29..bf9cb390 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -107,9 +107,8 @@ def __ensure_testcase_module(path: Text) -> NoReturn: 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): - # folder does not need to convert - return testcase_path, "" + if not os.path.isfile(testcase_path): + raise exceptions.ParamsError(f"Invalid testcase file path: {testcase_path}") testcase_path = __ensure_file_name(testcase_path) raw_file_name, file_suffix = os.path.splitext(os.path.basename(testcase_path)) From d1db909dfb9082c0e6de59748663689eb39c0032 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 17:09:16 +0800 Subject: [PATCH 16/30] fix: make sure generated pytest folder exists --- httprunner/make.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/httprunner/make.py b/httprunner/make.py index bf9cb390..f11a22d0 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -325,6 +325,9 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: } content = __TEMPLATE__.render(data) + testcase_python_dir = os.path.dirname(testcase_python_path) + if not os.path.exists(testcase_python_dir): + os.makedirs(testcase_python_dir) with open(testcase_python_path, "w", encoding="utf-8") as f: f.write(content) From ae3ebbd0db3caad10e2c576b16147f55a21bb40f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 18:41:28 +0800 Subject: [PATCH 17/30] change: ensure testcase file exists --- httprunner/make.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/httprunner/make.py b/httprunner/make.py index f11a22d0..4eb04972 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -76,6 +76,9 @@ def __ensure_absolute(path: Text) -> Text: else: absolute_path = os.path.join(project_meta.PWD, path) + if not os.path.isfile(absolute_path): + raise exceptions.ParamsError(f"Invalid testcase file path: {absolute_path}") + return absolute_path @@ -107,9 +110,6 @@ def __ensure_testcase_module(path: Text) -> NoReturn: def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: """convert single YAML/JSON testcase path to python file""" - if not os.path.isfile(testcase_path): - raise exceptions.ParamsError(f"Invalid testcase file path: {testcase_path}") - testcase_path = __ensure_file_name(testcase_path) raw_file_name, file_suffix = os.path.splitext(os.path.basename(testcase_path)) From 862f0b6e5256120e5bed61fbef444c036e28226f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 19:09:47 +0800 Subject: [PATCH 18/30] fix: handle cases when parent directory name includes dot/hyphen/space --- docs/CHANGELOG.md | 1 + .../__init__.py | 0 .../cookie_manipulation/__init__.py | 0 .../cookie_manipulation/hardcode.yml | 0 .../cookie_manipulation/hardcode_test.py | 0 .../cookie_manipulation/set_delete_cookies.py | 0 .../set_delete_cookies.yml | 0 .../set_delete_cookies_test.py | 0 .../debugtalk.py | 0 .../request.methods}/demo_testsuite.yml | 4 +- .../request.methods}/hardcode.yml | 0 .../request_with_functions.yml | 0 .../request_with_testcase_reference.yml | 2 +- .../request_with_variables.yml | 0 .../validate_with_functions.yml | 0 .../validate_with_variables.yml | 0 .../postman_echo/request_methods/__init__.py | 1 + .../postman_echo/request_methods/conftest.py | 61 ------------------- .../__init__.py | 0 .../request_with_functions_test.py | 2 +- .../request_with_testcase_reference_test.py | 2 +- .../request_methods/hardcode_test.py | 2 +- .../request_with_functions_test.py | 2 +- .../request_with_testcase_reference_test.py | 2 +- .../request_with_variables_test.py | 2 +- .../validate_with_functions_test.py | 2 +- .../validate_with_variables_test.py | 2 +- httprunner/make.py | 31 +++++++--- 28 files changed, 35 insertions(+), 81 deletions(-) rename examples/{postman_echo => postman-echo}/__init__.py (100%) rename examples/{postman_echo => postman-echo}/cookie_manipulation/__init__.py (100%) rename examples/{postman_echo => postman-echo}/cookie_manipulation/hardcode.yml (100%) rename examples/{postman_echo => postman-echo}/cookie_manipulation/hardcode_test.py (100%) rename examples/{postman_echo => postman-echo}/cookie_manipulation/set_delete_cookies.py (100%) rename examples/{postman_echo => postman-echo}/cookie_manipulation/set_delete_cookies.yml (100%) rename examples/{postman_echo => postman-echo}/cookie_manipulation/set_delete_cookies_test.py (100%) rename examples/{postman_echo => postman-echo}/debugtalk.py (100%) rename examples/{postman_echo/request_methods => postman-echo/request.methods}/demo_testsuite.yml (67%) rename examples/{postman_echo/request_methods => postman-echo/request.methods}/hardcode.yml (100%) rename examples/{postman_echo/request_methods => postman-echo/request.methods}/request_with_functions.yml (100%) rename examples/{postman_echo/request_methods => postman-echo/request.methods}/request_with_testcase_reference.yml (92%) rename examples/{postman_echo/request_methods => postman-echo/request.methods}/request_with_variables.yml (100%) rename examples/{postman_echo/request_methods => postman-echo/request.methods}/validate_with_functions.yml (100%) rename examples/{postman_echo/request_methods => postman-echo/request.methods}/validate_with_variables.yml (100%) delete mode 100644 examples/postman_echo/request_methods/conftest.py rename examples/postman_echo/request_methods/{demo_testsuite_yml => demo_testsuite}/__init__.py (100%) rename examples/postman_echo/request_methods/{demo_testsuite_yml => demo_testsuite}/request_with_functions_test.py (97%) rename examples/postman_echo/request_methods/{demo_testsuite_yml => demo_testsuite}/request_with_testcase_reference_test.py (96%) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8af01f1f..d6f55e08 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,6 +5,7 @@ **Fixed** - fix: miss formatting referenced testcase +- fix: handle cases when parent directory name includes dot/hyphen/space **Changed** diff --git a/examples/postman_echo/__init__.py b/examples/postman-echo/__init__.py similarity index 100% rename from examples/postman_echo/__init__.py rename to examples/postman-echo/__init__.py diff --git a/examples/postman_echo/cookie_manipulation/__init__.py b/examples/postman-echo/cookie_manipulation/__init__.py similarity index 100% rename from examples/postman_echo/cookie_manipulation/__init__.py rename to examples/postman-echo/cookie_manipulation/__init__.py diff --git a/examples/postman_echo/cookie_manipulation/hardcode.yml b/examples/postman-echo/cookie_manipulation/hardcode.yml similarity index 100% rename from examples/postman_echo/cookie_manipulation/hardcode.yml rename to examples/postman-echo/cookie_manipulation/hardcode.yml diff --git a/examples/postman_echo/cookie_manipulation/hardcode_test.py b/examples/postman-echo/cookie_manipulation/hardcode_test.py similarity index 100% rename from examples/postman_echo/cookie_manipulation/hardcode_test.py rename to examples/postman-echo/cookie_manipulation/hardcode_test.py diff --git a/examples/postman_echo/cookie_manipulation/set_delete_cookies.py b/examples/postman-echo/cookie_manipulation/set_delete_cookies.py similarity index 100% rename from examples/postman_echo/cookie_manipulation/set_delete_cookies.py rename to examples/postman-echo/cookie_manipulation/set_delete_cookies.py diff --git a/examples/postman_echo/cookie_manipulation/set_delete_cookies.yml b/examples/postman-echo/cookie_manipulation/set_delete_cookies.yml similarity index 100% rename from examples/postman_echo/cookie_manipulation/set_delete_cookies.yml rename to examples/postman-echo/cookie_manipulation/set_delete_cookies.yml diff --git a/examples/postman_echo/cookie_manipulation/set_delete_cookies_test.py b/examples/postman-echo/cookie_manipulation/set_delete_cookies_test.py similarity index 100% rename from examples/postman_echo/cookie_manipulation/set_delete_cookies_test.py rename to examples/postman-echo/cookie_manipulation/set_delete_cookies_test.py diff --git a/examples/postman_echo/debugtalk.py b/examples/postman-echo/debugtalk.py similarity index 100% rename from examples/postman_echo/debugtalk.py rename to examples/postman-echo/debugtalk.py diff --git a/examples/postman_echo/request_methods/demo_testsuite.yml b/examples/postman-echo/request.methods/demo_testsuite.yml similarity index 67% rename from examples/postman_echo/request_methods/demo_testsuite.yml rename to examples/postman-echo/request.methods/demo_testsuite.yml index 946d37d7..d30490fc 100644 --- a/examples/postman_echo/request_methods/demo_testsuite.yml +++ b/examples/postman-echo/request.methods/demo_testsuite.yml @@ -5,11 +5,11 @@ config: testcases: - name: request with functions - testcase: request_methods/request_with_functions.yml + testcase: request.methods/request_with_functions.yml variables: var1: testsuite_val1 - name: request with referenced testcase - testcase: request_methods/request_with_testcase_reference.yml + testcase: request.methods/request_with_testcase_reference.yml variables: var2: testsuite_val2 diff --git a/examples/postman_echo/request_methods/hardcode.yml b/examples/postman-echo/request.methods/hardcode.yml similarity index 100% rename from examples/postman_echo/request_methods/hardcode.yml rename to examples/postman-echo/request.methods/hardcode.yml diff --git a/examples/postman_echo/request_methods/request_with_functions.yml b/examples/postman-echo/request.methods/request_with_functions.yml similarity index 100% rename from examples/postman_echo/request_methods/request_with_functions.yml rename to examples/postman-echo/request.methods/request_with_functions.yml diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference.yml b/examples/postman-echo/request.methods/request_with_testcase_reference.yml similarity index 92% rename from examples/postman_echo/request_methods/request_with_testcase_reference.yml rename to examples/postman-echo/request.methods/request_with_testcase_reference.yml index 37871f2c..c66dd56d 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference.yml +++ b/examples/postman-echo/request.methods/request_with_testcase_reference.yml @@ -10,7 +10,7 @@ teststeps: name: request with functions variables: foo1: override_bar1 - testcase: request_methods/request_with_functions.yml + testcase: request.methods/request_with_functions.yml export: - session_foo2 - diff --git a/examples/postman_echo/request_methods/request_with_variables.yml b/examples/postman-echo/request.methods/request_with_variables.yml similarity index 100% rename from examples/postman_echo/request_methods/request_with_variables.yml rename to examples/postman-echo/request.methods/request_with_variables.yml diff --git a/examples/postman_echo/request_methods/validate_with_functions.yml b/examples/postman-echo/request.methods/validate_with_functions.yml similarity index 100% rename from examples/postman_echo/request_methods/validate_with_functions.yml rename to examples/postman-echo/request.methods/validate_with_functions.yml diff --git a/examples/postman_echo/request_methods/validate_with_variables.yml b/examples/postman-echo/request.methods/validate_with_variables.yml similarity index 100% rename from examples/postman_echo/request_methods/validate_with_variables.yml rename to examples/postman-echo/request.methods/validate_with_variables.yml diff --git a/examples/postman_echo/request_methods/__init__.py b/examples/postman_echo/request_methods/__init__.py index e69de29b..70cfba53 100644 --- a/examples/postman_echo/request_methods/__init__.py +++ b/examples/postman_echo/request_methods/__init__.py @@ -0,0 +1 @@ +# NOTICE: Generated By HttpRunner. DO NOT EDIT! diff --git a/examples/postman_echo/request_methods/conftest.py b/examples/postman_echo/request_methods/conftest.py deleted file mode 100644 index 788c2686..00000000 --- a/examples/postman_echo/request_methods/conftest.py +++ /dev/null @@ -1,61 +0,0 @@ -import uuid -from typing import List - -import pytest -from httprunner import Config, Step -from loguru import logger - - -@pytest.fixture(scope="session", autouse=True) -def session_fixture(request): - """setup and teardown each task""" - total_testcases_num = request.node.testscollected - testcases = [] - for item in request.node.items: - testcase = { - "name": item.cls.config.name, - "path": item.cls.config.path, - "node_id": item.nodeid, - } - testcases.append(testcase) - - logger.debug(f"collected {total_testcases_num} testcases: {testcases}") - - yield - - logger.debug(f"teardown task fixture") - - # teardown task - # TODO: upload task summary - - -@pytest.fixture(scope="function", autouse=True) -def testcase_fixture(request): - """setup and teardown each testcase""" - config: Config = request.cls.config - teststeps: List[Step] = request.cls.teststeps - - logger.debug(f"setup testcase fixture: {config.name} - {request.module.__name__}") - - def update_request_headers(steps, index): - for teststep in steps: - if teststep.request: - index += 1 - teststep.request.headers["X-Request-ID"] = f"{prefix}-{index}" - elif teststep.testcase and hasattr(teststep.testcase, "teststeps"): - update_request_headers(teststep.testcase.teststeps, index) - - # you can update testcase teststep like this - prefix = f"HRUN-{uuid.uuid4()}" - update_request_headers(teststeps, 0) - - yield - - logger.debug( - f"teardown testcase fixture: {config.name} - {request.module.__name__}" - ) - - summary = request.instance.get_summary() - logger.debug(f"testcase result summary: {summary}") - - # TODO: upload testcase summary diff --git a/examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py b/examples/postman_echo/request_methods/demo_testsuite/__init__.py similarity index 100% rename from examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py rename to examples/postman_echo/request_methods/demo_testsuite/__init__.py diff --git a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py b/examples/postman_echo/request_methods/demo_testsuite/request_with_functions_test.py similarity index 97% rename from examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py rename to examples/postman_echo/request_methods/demo_testsuite/request_with_functions_test.py index 474e99c2..f4cb1e6b 100644 --- a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/demo_testsuite/request_with_functions_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/request_with_functions.yml +# FROM: examples/postman-echo/request.methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase 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/request_with_testcase_reference_test.py similarity index 96% rename from examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py rename to examples/postman_echo/request_methods/demo_testsuite/request_with_testcase_reference_test.py index 35a113d2..d968ae68 100644 --- a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/demo_testsuite/request_with_testcase_reference_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml +# FROM: examples/postman-echo/request.methods/request_with_testcase_reference.yml import os import sys diff --git a/examples/postman_echo/request_methods/hardcode_test.py b/examples/postman_echo/request_methods/hardcode_test.py index a499659b..d296aac3 100644 --- a/examples/postman_echo/request_methods/hardcode_test.py +++ b/examples/postman_echo/request_methods/hardcode_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/hardcode.yml +# FROM: examples/postman-echo/request.methods/hardcode.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_functions_test.py b/examples/postman_echo/request_methods/request_with_functions_test.py index 340749f5..2ba75345 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/request_with_functions.yml +# FROM: examples/postman-echo/request.methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py index 75c211ff..8c5b4b8e 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml +# FROM: examples/postman-echo/request.methods/request_with_testcase_reference.yml import os import sys diff --git a/examples/postman_echo/request_methods/request_with_variables_test.py b/examples/postman_echo/request_methods/request_with_variables_test.py index 47eff628..f2f6ca44 100644 --- a/examples/postman_echo/request_methods/request_with_variables_test.py +++ b/examples/postman_echo/request_methods/request_with_variables_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/request_with_variables.yml +# FROM: examples/postman-echo/request.methods/request_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/validate_with_functions_test.py b/examples/postman_echo/request_methods/validate_with_functions_test.py index b077ebc8..e2c049fd 100644 --- a/examples/postman_echo/request_methods/validate_with_functions_test.py +++ b/examples/postman_echo/request_methods/validate_with_functions_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/validate_with_functions.yml +# FROM: examples/postman-echo/request.methods/validate_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/validate_with_variables_test.py b/examples/postman_echo/request_methods/validate_with_variables_test.py index c333f462..72bd3aff 100644 --- a/examples/postman_echo/request_methods/validate_with_variables_test.py +++ b/examples/postman_echo/request_methods/validate_with_variables_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman_echo/request_methods/validate_with_variables.yml +# FROM: examples/postman-echo/request.methods/validate_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/httprunner/make.py b/httprunner/make.py index 4eb04972..58e50fd8 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -108,6 +108,17 @@ def __ensure_testcase_module(path: Text) -> NoReturn: f.write("# NOTICE: Generated By HttpRunner. DO NOT EDIT!\n") +def __ensure_dir_path_valid(dir_path: Text) -> Text: + # handle cases when parent directory name includes dot/hyphen/space + relative_dir_path = dir_path[len(os.getcwd()) + 1:] + relative_dir_path = ( + relative_dir_path.replace(" ", "_").replace(".", "_").replace("-", "_") + ) + dir_path = os.path.join(os.getcwd(), relative_dir_path) + os.makedirs(dir_path, exist_ok=True) + return dir_path + + def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: """convert single YAML/JSON testcase path to python file""" testcase_path = __ensure_file_name(testcase_path) @@ -121,6 +132,7 @@ def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: file_name = raw_file_name.replace(" ", "_").replace(".", "_").replace("-", "_") testcase_dir = os.path.dirname(testcase_path) + testcase_dir = __ensure_dir_path_valid(testcase_dir) testcase_python_path = os.path.join(testcase_dir, f"{file_name}_test.py") # convert title case, e.g. request_with_variables => RequestWithVariables @@ -325,9 +337,6 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: } content = __TEMPLATE__.render(data) - testcase_python_dir = os.path.dirname(testcase_python_path) - if not os.path.exists(testcase_python_dir): - os.makedirs(testcase_python_dir) with open(testcase_python_path, "w", encoding="utf-8") as f: f.write(content) @@ -345,7 +354,7 @@ def make_testsuite(testsuite: Dict) -> NoReturn: load_testsuite(testsuite) testsuite_config = testsuite["config"] - testsuite_path = testsuite_config["path"] + testsuite_path = __ensure_absolute(testsuite_config["path"]) testsuite_variables = testsuite_config.get("variables", {}) if isinstance(testsuite_variables, Text): @@ -358,11 +367,15 @@ def make_testsuite(testsuite: Dict) -> NoReturn: logger.info(f"start to make testsuite: {testsuite_path}") # create directory with testsuite file name, put its testcases under this directory - testsuite_dir = os.path.join( - os.path.dirname(testsuite_path), - os.path.basename(testsuite_path).replace(".", "_"), - ) - os.makedirs(testsuite_dir, exist_ok=True) + testsuite_file_name, file_suffix = os.path.splitext(testsuite_path) + + file_suffix = file_suffix.lower() + if file_suffix not in [".json", ".yml", ".yaml", ".har"]: + raise exceptions.ParamsError( + "testsuite file should have .yaml/.yml/.json suffix" + ) + + testsuite_dir = __ensure_dir_path_valid(testsuite_file_name) for testcase in testsuite["testcases"]: # get referenced testcase content From 2a9a05af9bfcfef0a33eac31f89085b5b10a1234 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 5 Jun 2020 22:34:27 +0800 Subject: [PATCH 19/30] change: add ProjectMeta debugtalk_path --- httprunner/loader.py | 4 +--- httprunner/models.py | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/httprunner/loader.py b/httprunner/loader.py index 880d5918..a7a9fabd 100644 --- a/httprunner/loader.py +++ b/httprunner/loader.py @@ -427,8 +427,6 @@ def load_project_meta(test_path: Text, reload: bool = False) -> ProjectMeta: # locate PWD and load debugtalk.py functions project_meta.PWD = project_working_directory project_meta.functions = debugtalk_functions - project_meta.test_path = os.path.abspath(test_path)[ - len(project_working_directory) + 1 : - ] + project_meta.debugtalk_path = debugtalk_path return project_meta diff --git a/httprunner/models.py b/httprunner/models.py index 449f168f..cac1a32c 100644 --- a/httprunner/models.py +++ b/httprunner/models.py @@ -81,10 +81,10 @@ class TestCase(BaseModel): class ProjectMeta(BaseModel): debugtalk_py: Text = "" # debugtalk.py file content - functions: FunctionsMapping = {} + debugtalk_path: Text = "" # debugtalk.py file path + functions: FunctionsMapping = {} # functions defined in debugtalk.py env: Env = {} - PWD: Text = os.getcwd() - test_path: Text = None # run with specified test path + PWD: Text = os.getcwd() # project working directory, the path debugtalk.py located class TestsMapping(BaseModel): From e8275ab38ae6f6fc65a32d79057934fc2c28a14c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 6 Jun 2020 00:32:38 +0800 Subject: [PATCH 20/30] refactor: ensure_file_path_valid --- .../cookie_manipulation/__init__.py | 0 .../cookie_manipulation/hardcode_test.py | 54 ----------- .../cookie_manipulation/set_delete_cookies.py | 62 ------------ .../set_delete_cookies_test.py | 59 ----------- .../postman-echo/request.methods/conftest.py | 61 ++++++++++++ .../__init__.py | 0 .../request_with_functions_test.py | 0 .../request_with_testcase_reference_test.py | 0 httprunner/make.py | 97 ++++++++++--------- tests/compat_test.py | 16 +-- tests/loader_test.py | 2 +- tests/make_test.py | 69 ++++++++----- tests/runner_test.py | 4 +- 13 files changed, 168 insertions(+), 256 deletions(-) delete mode 100644 examples/postman-echo/cookie_manipulation/__init__.py delete mode 100644 examples/postman-echo/cookie_manipulation/hardcode_test.py delete mode 100644 examples/postman-echo/cookie_manipulation/set_delete_cookies.py delete mode 100644 examples/postman-echo/cookie_manipulation/set_delete_cookies_test.py create mode 100644 examples/postman-echo/request.methods/conftest.py rename examples/postman_echo/request_methods/{demo_testsuite => demo_testsuite_yml}/__init__.py (100%) rename examples/postman_echo/request_methods/{demo_testsuite => demo_testsuite_yml}/request_with_functions_test.py (100%) rename examples/postman_echo/request_methods/{demo_testsuite => demo_testsuite_yml}/request_with_testcase_reference_test.py (100%) diff --git a/examples/postman-echo/cookie_manipulation/__init__.py b/examples/postman-echo/cookie_manipulation/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/postman-echo/cookie_manipulation/hardcode_test.py b/examples/postman-echo/cookie_manipulation/hardcode_test.py deleted file mode 100644 index 000d0404..00000000 --- a/examples/postman-echo/cookie_manipulation/hardcode_test.py +++ /dev/null @@ -1,54 +0,0 @@ -# NOTICE: Generated By HttpRunner. DO NOT EDIT! -# FROM: examples/postman_echo/cookie_manipulation/hardcode.yml -from httprunner import HttpRunner, TConfig, TStep - - -class TestCaseHardcode(HttpRunner): - config = TConfig( - **{ - "name": "set & delete cookies.", - "base_url": "https://postman-echo.com", - "verify": False, - "export": ["cookie_foo1", "cookie_foo3"], - "path": "examples/postman_echo/cookie_manipulation/hardcode_test.py", - } - ) - - teststeps = [ - TStep( - **{ - "name": "set cookie foo1 & foo2 & foo3", - "request": { - "method": "GET", - "url": "/cookies/set", - "params": {"foo1": "bar1", "foo2": "bar2"}, - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "extract": {"cookie_foo1": "$.cookies.foo1"}, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["cookies.foo1", "bar1"]}, - ], - } - ), - TStep( - **{ - "name": "delete cookie foo2", - "request": { - "method": "GET", - "url": "/cookies/delete?foo2", - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"ne": ["$.cookies.foo1", "$foo1"]}, - {"eq": ["$.cookies.foo1", "$cookie_foo1"]}, - {"eq": ["$.cookies.foo3", "$cookie_foo3"]}, - ], - } - ), - ] - - -if __name__ == "__main__": - TestCaseHardcode().test_start() diff --git a/examples/postman-echo/cookie_manipulation/set_delete_cookies.py b/examples/postman-echo/cookie_manipulation/set_delete_cookies.py deleted file mode 100644 index 43ddabfb..00000000 --- a/examples/postman-echo/cookie_manipulation/set_delete_cookies.py +++ /dev/null @@ -1,62 +0,0 @@ -import unittest -import requests - -from httprunner.runner import HttpRunner -from httprunner.models import TConfig, TStep - - -class TestCaseSetDeleteCookies(unittest.TestCase): - config = TConfig( - **{ - "name": "set & delete cookies.", - "base_url": "https://postman-echo.com", - "variables": {"foo1": "bar1", "foo2": "bar2"}, - "verify": False, - "export": ["cookie_foo1", "cookie_foo3"], - } - ) - - teststeps = [ - TStep( - **{ - "name": "set cookie foo1 & foo2 & foo3", - "variables": {"foo3": "bar3"}, - "request": { - "method": "GET", - "url": "/cookies/set", - "params": {"foo1": "bar111", "foo2": "$foo2", "foo3": "$foo3"}, - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "extract": { - "cookie_foo1": "$.cookies.foo1", - "cookie_foo3": "$.cookies.foo3", - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["$.cookies.foo3", "$foo3"]}, - ], - } - ), - TStep( - **{ - "name": "delete cookie foo2", - "request": { - "method": "GET", - "url": "/cookies/delete?foo2", - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"ne": ["$.cookies.foo1", "$foo1"]}, - {"eq": ["$.cookies.foo1", "$cookie_foo1"]}, - {"eq": ["$.cookies.foo3", "$cookie_foo3"]}, - ], - } - ), - ] - - def test_start(self): - s = requests.Session() - HttpRunner(self.config, self.teststeps, session=s).with_variables( - foo1="bar123", foo2="bar22" - ).run() diff --git a/examples/postman-echo/cookie_manipulation/set_delete_cookies_test.py b/examples/postman-echo/cookie_manipulation/set_delete_cookies_test.py deleted file mode 100644 index 540a2eb1..00000000 --- a/examples/postman-echo/cookie_manipulation/set_delete_cookies_test.py +++ /dev/null @@ -1,59 +0,0 @@ -# NOTICE: Generated By HttpRunner. DO NOT EDIT! -# FROM: examples/postman_echo/cookie_manipulation/set_delete_cookies.yml -from httprunner import HttpRunner, TConfig, TStep - - -class TestCaseSetDeleteCookies(HttpRunner): - config = TConfig( - **{ - "name": "set & delete cookies.", - "variables": {"foo1": "bar1", "foo2": "bar2"}, - "base_url": "https://postman-echo.com", - "verify": False, - "export": ["cookie_foo1", "cookie_foo3"], - "path": "examples/postman_echo/cookie_manipulation/set_delete_cookies_test.py", - } - ) - - teststeps = [ - TStep( - **{ - "name": "set cookie foo1 & foo2 & foo3", - "variables": {"foo3": "bar3"}, - "request": { - "method": "GET", - "url": "/cookies/set", - "params": {"foo1": "bar111", "foo2": "$foo2", "foo3": "$foo3"}, - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "extract": { - "cookie_foo1": "$.cookies.foo1", - "cookie_foo3": "$.cookies.foo3", - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"ne": ["$.cookies.foo3", "$foo3"]}, - ], - } - ), - TStep( - **{ - "name": "delete cookie foo2", - "request": { - "method": "GET", - "url": "/cookies/delete?foo2", - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"ne": ["$.cookies.foo1", "$foo1"]}, - {"eq": ["$.cookies.foo1", "$cookie_foo1"]}, - {"eq": ["$.cookies.foo3", "$cookie_foo3"]}, - ], - } - ), - ] - - -if __name__ == "__main__": - TestCaseSetDeleteCookies().test_start() diff --git a/examples/postman-echo/request.methods/conftest.py b/examples/postman-echo/request.methods/conftest.py new file mode 100644 index 00000000..788c2686 --- /dev/null +++ b/examples/postman-echo/request.methods/conftest.py @@ -0,0 +1,61 @@ +import uuid +from typing import List + +import pytest +from httprunner import Config, Step +from loguru import logger + + +@pytest.fixture(scope="session", autouse=True) +def session_fixture(request): + """setup and teardown each task""" + total_testcases_num = request.node.testscollected + testcases = [] + for item in request.node.items: + testcase = { + "name": item.cls.config.name, + "path": item.cls.config.path, + "node_id": item.nodeid, + } + testcases.append(testcase) + + logger.debug(f"collected {total_testcases_num} testcases: {testcases}") + + yield + + logger.debug(f"teardown task fixture") + + # teardown task + # TODO: upload task summary + + +@pytest.fixture(scope="function", autouse=True) +def testcase_fixture(request): + """setup and teardown each testcase""" + config: Config = request.cls.config + teststeps: List[Step] = request.cls.teststeps + + logger.debug(f"setup testcase fixture: {config.name} - {request.module.__name__}") + + def update_request_headers(steps, index): + for teststep in steps: + if teststep.request: + index += 1 + teststep.request.headers["X-Request-ID"] = f"{prefix}-{index}" + elif teststep.testcase and hasattr(teststep.testcase, "teststeps"): + update_request_headers(teststep.testcase.teststeps, index) + + # you can update testcase teststep like this + prefix = f"HRUN-{uuid.uuid4()}" + update_request_headers(teststeps, 0) + + yield + + logger.debug( + f"teardown testcase fixture: {config.name} - {request.module.__name__}" + ) + + summary = request.instance.get_summary() + logger.debug(f"testcase result summary: {summary}") + + # TODO: upload testcase summary diff --git a/examples/postman_echo/request_methods/demo_testsuite/__init__.py b/examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py similarity index 100% rename from examples/postman_echo/request_methods/demo_testsuite/__init__.py rename to examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py diff --git a/examples/postman_echo/request_methods/demo_testsuite/request_with_functions_test.py b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py similarity index 100% rename from examples/postman_echo/request_methods/demo_testsuite/request_with_functions_test.py rename to examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py diff --git a/examples/postman_echo/request_methods/demo_testsuite/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py similarity index 100% rename from examples/postman_echo/request_methods/demo_testsuite/request_with_testcase_reference_test.py rename to examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py diff --git a/httprunner/make.py b/httprunner/make.py index 58e50fd8..53f70d48 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -57,17 +57,6 @@ if __name__ == "__main__": ) -def __ensure_file_name(path: Text) -> Text: - """ ensure file name not startswith digit - testcases/19.json => testcases/T19.json - """ - filename = os.path.basename(path) - if filename[0] in string.digits: - path = os.path.join(os.path.dirname(path), f"T{filename}") - - return path - - def __ensure_absolute(path: Text) -> Text: project_meta = load_project_meta(path) @@ -108,32 +97,53 @@ def __ensure_testcase_module(path: Text) -> NoReturn: f.write("# NOTICE: Generated By HttpRunner. DO NOT EDIT!\n") -def __ensure_dir_path_valid(dir_path: Text) -> Text: - # handle cases when parent directory name includes dot/hyphen/space - relative_dir_path = dir_path[len(os.getcwd()) + 1:] - relative_dir_path = ( - relative_dir_path.replace(" ", "_").replace(".", "_").replace("-", "_") - ) - dir_path = os.path.join(os.getcwd(), relative_dir_path) - os.makedirs(dir_path, exist_ok=True) - return dir_path +def ensure_file_path_valid(file_path: Text) -> Text: + """ ensure file path valid for pytest + Args: + file_path: absolute or relative file path -def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: - """convert single YAML/JSON testcase path to python file""" - testcase_path = __ensure_file_name(testcase_path) - raw_file_name, file_suffix = os.path.splitext(os.path.basename(testcase_path)) + Returns: + ensured valid absolute file path + + """ + raw_file_name, file_suffix = os.path.splitext(file_path) file_suffix = file_suffix.lower() if file_suffix not in [".json", ".yml", ".yaml", ".har"]: raise exceptions.ParamsError( - "testcase file should have .yaml/.yml/.json suffix" + "testcase/testsuite file should have .yaml/.yml/.json/.har suffix" ) - file_name = raw_file_name.replace(" ", "_").replace(".", "_").replace("-", "_") - testcase_dir = os.path.dirname(testcase_path) - testcase_dir = __ensure_dir_path_valid(testcase_dir) - testcase_python_path = os.path.join(testcase_dir, f"{file_name}_test.py") + if os.path.isabs(file_path): + raw_file_relative_name = raw_file_name[len(os.getcwd()) + 1 :] + else: + raw_file_relative_name = raw_file_name + + path_names = [] + for name in raw_file_relative_name.split(os.sep): + + if name[0] in string.digits: + # ensure file name not startswith digit + # 19 => T19, 2C => T2C + name = f"T{name}" + + # handle cases when directory name includes dot/hyphen/space + name = name.replace(" ", "_").replace(".", "_").replace("-", "_") + + path_names.append(name) + + new_file_path = os.path.join(os.getcwd(), f"{os.sep.join(path_names)}{file_suffix}") + return new_file_path + + +def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: + """convert single YAML/JSON testcase path to python file""" + testcase_new_path = ensure_file_path_valid(testcase_path) + + dir_path = os.path.dirname(testcase_new_path) + file_name, _ = os.path.splitext(os.path.basename(testcase_new_path)) + testcase_python_path = os.path.join(dir_path, f"{file_name}_test.py") # convert title case, e.g. request_with_variables => RequestWithVariables name_in_title_case = file_name.title().replace("_", "") @@ -273,10 +283,10 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: # validate testcase format load_testcase(testcase) - testcase_path = __ensure_absolute(testcase["config"]["path"]) - logger.info(f"start to make testcase: {testcase_path}") + testcase_abs_path = __ensure_absolute(testcase["config"]["path"]) + logger.info(f"start to make testcase: {testcase_abs_path}") - testcase_python_path, testcase_cls_name = convert_testcase_path(testcase_path) + testcase_python_path, testcase_cls_name = convert_testcase_path(testcase_abs_path) if dir_path: testcase_python_path = os.path.join( dir_path, os.path.basename(testcase_python_path) @@ -293,7 +303,7 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: config.setdefault("variables", {}) if isinstance(config["variables"], Text): # get variables by function, e.g. ${get_variables()} - project_meta = load_project_meta(testcase_path) + project_meta = load_project_meta(testcase_abs_path) config["variables"] = parse_data( config["variables"], {}, project_meta.functions ) @@ -327,7 +337,7 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: data = { "version": __version__, - "testcase_path": __ensure_cwd_relative(testcase_path), + "testcase_path": __ensure_cwd_relative(testcase_abs_path), "class_name": f"TestCase{testcase_cls_name}", "imports_list": imports_list, "config_chain_style": make_config_chain_style(config), @@ -337,6 +347,10 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: } content = __TEMPLATE__.render(data) + # ensure new file's directory exists + dir_path = os.path.dirname(testcase_python_path) + os.makedirs(dir_path, exist_ok=True) + with open(testcase_python_path, "w", encoding="utf-8") as f: f.write(content) @@ -354,7 +368,7 @@ def make_testsuite(testsuite: Dict) -> NoReturn: load_testsuite(testsuite) testsuite_config = testsuite["config"] - testsuite_path = __ensure_absolute(testsuite_config["path"]) + testsuite_path = testsuite_config["path"] testsuite_variables = testsuite_config.get("variables", {}) if isinstance(testsuite_variables, Text): @@ -367,15 +381,10 @@ def make_testsuite(testsuite: Dict) -> NoReturn: logger.info(f"start to make testsuite: {testsuite_path}") # create directory with testsuite file name, put its testcases under this directory - testsuite_file_name, file_suffix = os.path.splitext(testsuite_path) - - file_suffix = file_suffix.lower() - if file_suffix not in [".json", ".yml", ".yaml", ".har"]: - raise exceptions.ParamsError( - "testsuite file should have .yaml/.yml/.json suffix" - ) - - testsuite_dir = __ensure_dir_path_valid(testsuite_file_name) + testsuite_path = ensure_file_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 diff --git a/tests/compat_test.py b/tests/compat_test.py index 1b2dba3e..04b55988 100644 --- a/tests/compat_test.py +++ b/tests/compat_test.py @@ -142,30 +142,30 @@ class TestCompat(unittest.TestCase): ) def test_ensure_cli_args(self): - args1 = ["examples/postman_echo/request_methods/hardcode.yml", "--failfast"] + args1 = ["examples/postman-echo/request.methods/hardcode.yml", "--failfast"] self.assertEqual( compat.ensure_cli_args(args1), - ["examples/postman_echo/request_methods/hardcode.yml"], + ["examples/postman-echo/request.methods/hardcode.yml"], ) - args2 = ["examples/postman_echo/request_methods/hardcode.yml", "--save-tests"] + args2 = ["examples/postman-echo/request.methods/hardcode.yml", "--save-tests"] self.assertEqual( compat.ensure_cli_args(args2), - ["examples/postman_echo/request_methods/hardcode.yml"], + ["examples/postman-echo/request.methods/hardcode.yml"], ) self.assertTrue( os.path.isfile("examples/postman_echo/request_methods/conftest.py") ) args3 = [ - "examples/postman_echo/request_methods/hardcode.yml", + "examples/postman-echo/request.methods/hardcode.yml", "--report-file", "report.html", ] self.assertEqual( compat.ensure_cli_args(args3), [ - "examples/postman_echo/request_methods/hardcode.yml", + "examples/postman-echo/request.methods/hardcode.yml", "--html", "report.html", "--self-contained-html", @@ -173,7 +173,7 @@ class TestCompat(unittest.TestCase): ) args4 = [ - "examples/postman_echo/request_methods/hardcode.yml", + "examples/postman-echo/request.methods/hardcode.yml", "--failfast", "--save-tests", "--report-file", @@ -182,7 +182,7 @@ class TestCompat(unittest.TestCase): self.assertEqual( compat.ensure_cli_args(args4), [ - "examples/postman_echo/request_methods/hardcode.yml", + "examples/postman-echo/request.methods/hardcode.yml", "--html", "report.html", "--self-contained-html", diff --git a/tests/loader_test.py b/tests/loader_test.py index c5e2ab36..27e216ec 100644 --- a/tests/loader_test.py +++ b/tests/loader_test.py @@ -6,7 +6,7 @@ from httprunner import exceptions, loader class TestLoader(unittest.TestCase): def test_load_testcase_file(self): - path = "examples/postman_echo/request_methods/request_with_variables.yml" + path = "examples/postman-echo/request.methods/request_with_variables.yml" testcase_obj = loader.load_testcase_file(path) self.assertEqual( testcase_obj.config.name, "request methods testcase with variables" diff --git a/tests/make_test.py b/tests/make_test.py index 8b0e9e44..0cb75282 100644 --- a/tests/make_test.py +++ b/tests/make_test.py @@ -8,12 +8,13 @@ from httprunner.make import ( make_config_chain_style, make_teststep_chain_style, pytest_files_run_set, + ensure_file_path_valid, ) class TestMake(unittest.TestCase): def test_make_testcase(self): - path = ["examples/postman_echo/request_methods/request_with_variables.yml"] + path = ["examples/postman-echo/request.methods/request_with_variables.yml"] testcase_python_list = main_make(path) self.assertEqual( testcase_python_list[0], @@ -25,7 +26,7 @@ class TestMake(unittest.TestCase): def test_make_testcase_with_ref(self): path = [ - "examples/postman_echo/request_methods/request_with_testcase_reference.yml" + "examples/postman-echo/request.methods/request_with_testcase_reference.yml" ] pytest_files_made_cache_mapping.clear() pytest_files_run_set.clear() @@ -56,7 +57,7 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( ) def test_make_testcase_folder(self): - path = ["examples/postman_echo/request_methods/"] + path = ["examples/postman-echo/request.methods/"] testcase_python_list = main_make(path) self.assertIn( os.path.join( @@ -66,42 +67,58 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( testcase_python_list, ) + def test_ensure_file_path_valid(self): + self.assertEqual( + ensure_file_path_valid( + "examples/postman-echo/request.methods/hardcode.yml" + ), + os.path.join( + os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" + ), + ) + self.assertEqual( + ensure_file_path_valid( + os.path.join(os.getcwd(), "postman-echo/request.methods/hardcode.yml") + ), + os.path.join(os.getcwd(), "postman_echo/request_methods/hardcode.yml"), + ) + self.assertEqual( + ensure_file_path_valid( + "examples/postman echo/request methods/hardcode.yml" + ), + os.path.join( + os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" + ), + ) + self.assertEqual( + ensure_file_path_valid("1/2B/3.yml"), + os.path.join(os.getcwd(), "T1/T2B/T3.yml"), + ) + def test_convert_testcase_path(self): self.assertEqual( - convert_testcase_path("mubu.login.yml")[0], "mubu_login_test.py" + convert_testcase_path("mubu.login.yml"), + (os.path.join(os.getcwd(), "mubu_login_test.py"), "MubuLogin"), ) self.assertEqual( - convert_testcase_path("/path/to/mubu.login.yml")[0], - "/path/to/mubu_login_test.py", + convert_testcase_path(os.path.join(os.getcwd(), "path/to/mubu.login.yml")), + (os.path.join(os.getcwd(), "path/to/mubu_login_test.py"), "MubuLogin"), ) self.assertEqual( - convert_testcase_path("/path/to 2/mubu.login.yml")[0], - "/path/to 2/mubu_login_test.py", + convert_testcase_path("path/to 2/mubu.login.yml"), + (os.path.join(os.getcwd(), "path/to_2/mubu_login_test.py"), "MubuLogin"), ) self.assertEqual( - convert_testcase_path("/path/to 2/mubu.login.yml")[1], "MubuLogin" + convert_testcase_path("path/to-2/mubu login.yml"), + (os.path.join(os.getcwd(), "path/to_2/mubu_login_test.py"), "MubuLogin"), ) self.assertEqual( - convert_testcase_path("mubu login.yml")[0], "mubu_login_test.py" + convert_testcase_path("path/to.2/幕布login.yml"), + (os.path.join(os.getcwd(), "path/to_2/幕布login_test.py"), "幕布Login"), ) - self.assertEqual( - convert_testcase_path("/path/to 2/mubu login.yml")[1], "MubuLogin" - ) - self.assertEqual( - convert_testcase_path("/path/to 2/mubu-login.yml")[0], - "/path/to 2/mubu_login_test.py", - ) - self.assertEqual( - convert_testcase_path("/path/to 2/mubu-login.yml")[1], "MubuLogin" - ) - self.assertEqual( - convert_testcase_path("/path/to 2/幕布login.yml")[0], - "/path/to 2/幕布login_test.py", - ) - self.assertEqual(convert_testcase_path("/path/to/幕布login.yml")[1], "幕布Login") def test_make_testsuite(self): - path = ["examples/postman_echo/request_methods/demo_testsuite.yml"] + path = ["examples/postman-echo/request.methods/demo_testsuite.yml"] pytest_files_made_cache_mapping.clear() pytest_files_run_set.clear() testcase_python_list = main_make(path) diff --git a/tests/runner_test.py b/tests/runner_test.py index f0dc4a87..66cef1c4 100644 --- a/tests/runner_test.py +++ b/tests/runner_test.py @@ -9,7 +9,7 @@ class TestHttpRunner(unittest.TestCase): def test_run_testcase_by_path_request_only(self): self.runner.run_path( - "examples/postman_echo/request_methods/request_with_functions.yml" + "examples/postman-echo/request.methods/request_with_functions.yml" ) result = self.runner.get_summary() self.assertTrue(result.success) @@ -19,7 +19,7 @@ class TestHttpRunner(unittest.TestCase): def test_run_testcase_by_path_ref_testcase(self): self.runner.run_path( - "examples/postman_echo/request_methods/request_with_testcase_reference.yml" + "examples/postman-echo/request.methods/request_with_testcase_reference.yml" ) result = self.runner.get_summary() self.assertTrue(result.success) From 9a4ee8f3a04399022b72403cadd6c60c40caf498 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 6 Jun 2020 09:38:00 +0800 Subject: [PATCH 21/30] fix: ensure project meta files exist in generated pytest folder files --- httprunner/loader.py | 5 ++++- httprunner/make.py | 32 ++++++++++++++++++++++++++------ httprunner/models.py | 1 + 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/httprunner/loader.py b/httprunner/loader.py index a7a9fabd..b0f1dc96 100644 --- a/httprunner/loader.py +++ b/httprunner/loader.py @@ -416,7 +416,10 @@ def load_project_meta(test_path: Text, reload: bool = False) -> ProjectMeta: # environment variable maybe loaded in debugtalk.py # thus .env file should be loaded before loading debugtalk.py dot_env_path = os.path.join(project_working_directory, ".env") - project_meta.env = load_dot_env_file(dot_env_path) + dot_env = load_dot_env_file(dot_env_path) + if dot_env: + project_meta.env = dot_env + project_meta.dot_env_path = dot_env_path if debugtalk_path: # load debugtalk.py functions diff --git a/httprunner/make.py b/httprunner/make.py index 53f70d48..82faf410 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -1,6 +1,7 @@ import os import string import subprocess +from shutil import copyfile from typing import Text, List, Tuple, Dict, Set, NoReturn import jinja2 @@ -97,6 +98,28 @@ def __ensure_testcase_module(path: Text) -> NoReturn: f.write("# NOTICE: Generated By HttpRunner. DO NOT EDIT!\n") +def __ensure_project_meta_files(tests_path: Text) -> NoReturn: + """ ensure project meta files exist in generated pytest folder files + include debugtalk.py and .env + """ + project_meta = load_project_meta(tests_path) + + # handle cases when generated pytest directory are different from original yaml/json testcases + debugtalk_path = project_meta.debugtalk_path + if debugtalk_path: + debugtalk_new_path = ensure_file_path_valid(debugtalk_path) + if debugtalk_new_path != debugtalk_path: + logger.info(f"copy debugtalk.py to {debugtalk_new_path}") + copyfile(debugtalk_path, debugtalk_new_path) + + dot_csv_path = project_meta.dot_env_path + if dot_csv_path: + dot_csv_new_path = ensure_file_path_valid(dot_csv_path) + if dot_csv_new_path != dot_csv_path: + logger.info(f"copy .env to {dot_csv_new_path}") + copyfile(dot_csv_path, dot_csv_new_path) + + def ensure_file_path_valid(file_path: Text) -> Text: """ ensure file path valid for pytest @@ -108,12 +131,7 @@ def ensure_file_path_valid(file_path: Text) -> Text: """ raw_file_name, file_suffix = os.path.splitext(file_path) - file_suffix = file_suffix.lower() - if file_suffix not in [".json", ".yml", ".yaml", ".har"]: - raise exceptions.ParamsError( - "testcase/testsuite file should have .yaml/.yml/.json/.har suffix" - ) if os.path.isabs(file_path): raw_file_relative_name = raw_file_name[len(os.getcwd()) + 1 :] @@ -349,7 +367,8 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: # ensure new file's directory exists dir_path = os.path.dirname(testcase_python_path) - os.makedirs(dir_path, exist_ok=True) + if not os.path.exists(dir_path): + os.makedirs(dir_path) with open(testcase_python_path, "w", encoding="utf-8") as f: f.write(content) @@ -479,6 +498,7 @@ def main_make(tests_paths: List[Text]) -> List[Text]: tests_path = os.path.join(os.getcwd(), tests_path) __make(tests_path) + __ensure_project_meta_files(tests_path) # format pytest files pytest_files_format_list = pytest_files_made_cache_mapping.keys() diff --git a/httprunner/models.py b/httprunner/models.py index cac1a32c..59fbfcca 100644 --- a/httprunner/models.py +++ b/httprunner/models.py @@ -82,6 +82,7 @@ class TestCase(BaseModel): class ProjectMeta(BaseModel): debugtalk_py: Text = "" # debugtalk.py file content debugtalk_path: Text = "" # debugtalk.py file path + dot_env_path: Text = "" # .env file path functions: FunctionsMapping = {} # functions defined in debugtalk.py env: Env = {} PWD: Text = os.getcwd() # project working directory, the path debugtalk.py located From 6471b006cb9f1463bd48d5d585ad50479ac8f3cb Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 6 Jun 2020 10:05:57 +0800 Subject: [PATCH 22/30] change: format copied debugtalk.py --- examples/postman_echo/debugtalk.py | 13 +++++++++++++ httprunner/make.py | 3 +++ 2 files changed, 16 insertions(+) create mode 100644 examples/postman_echo/debugtalk.py diff --git a/examples/postman_echo/debugtalk.py b/examples/postman_echo/debugtalk.py new file mode 100644 index 00000000..af8b22eb --- /dev/null +++ b/examples/postman_echo/debugtalk.py @@ -0,0 +1,13 @@ +from httprunner import __version__ + + +def get_httprunner_version(): + return __version__ + + +def sum_two(m, n): + return m + n + + +def get_variables(): + return {"foo1": "session_bar1"} diff --git a/httprunner/make.py b/httprunner/make.py index 82faf410..83d5eef8 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -112,6 +112,9 @@ def __ensure_project_meta_files(tests_path: Text) -> NoReturn: logger.info(f"copy debugtalk.py to {debugtalk_new_path}") copyfile(debugtalk_path, debugtalk_new_path) + global pytest_files_made_cache_mapping + pytest_files_made_cache_mapping[debugtalk_new_path] = "" + dot_csv_path = project_meta.dot_env_path if dot_csv_path: dot_csv_new_path = ensure_file_path_valid(dot_csv_path) From e2703fff32df68c14708adb1d1bc1cb18b28870f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 6 Jun 2020 21:17:00 +0800 Subject: [PATCH 23/30] change: rename PWD to project RootDir --- httprunner/compat.py | 6 ++--- httprunner/ext/uploader/__init__.py | 2 +- httprunner/loader.py | 40 ++++++++++++++--------------- httprunner/make.py | 2 +- httprunner/models.py | 2 +- httprunner/runner.py | 6 +++-- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/httprunner/compat.py b/httprunner/compat.py index f907c113..dd001804 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -220,7 +220,7 @@ def generate_conftest_for_summary(args: List): sys.exit(1) project_meta = load_project_meta(test_path) - conftest_path = os.path.join(project_meta.PWD, "conftest.py") + conftest_path = os.path.join(project_meta.RootDir, "conftest.py") if os.path.isfile(conftest_path): return @@ -291,8 +291,8 @@ def session_fixture(request): ''' test_path = os.path.abspath(test_path) - logs_dir_path = os.path.join(project_meta.PWD, "logs") - test_path_relative_path = test_path[len(project_meta.PWD) + 1 :] + logs_dir_path = os.path.join(project_meta.RootDir, "logs") + test_path_relative_path = test_path[len(project_meta.RootDir) + 1 :] if os.path.isdir(test_path): file_foder_path = os.path.join(logs_dir_path, test_path_relative_path) diff --git a/httprunner/ext/uploader/__init__.py b/httprunner/ext/uploader/__init__.py index 8768f8b2..4b242502 100644 --- a/httprunner/ext/uploader/__init__.py +++ b/httprunner/ext/uploader/__init__.py @@ -139,7 +139,7 @@ def multipart_encoder(**kwargs): project_meta = load_project_meta(os.getcwd()) - _file_path = os.path.join(project_meta.PWD, value) + _file_path = os.path.join(project_meta.RootDir, value) is_exists_file = os.path.isfile(_file_path) if is_exists_file: diff --git a/httprunner/loader.py b/httprunner/loader.py index b0f1dc96..ff37c15c 100644 --- a/httprunner/loader.py +++ b/httprunner/loader.py @@ -176,7 +176,7 @@ def load_csv_file(csv_file: Text) -> List[Dict]: raise exceptions.MyBaseFailure("load_project_meta() has not been called!") # make compatible with Windows/Linux - csv_file = os.path.join(project_meta.PWD, *csv_file.split("/")) + csv_file = os.path.join(project_meta.RootDir, *csv_file.split("/")) if not os.path.isfile(csv_file): # file path not exist @@ -327,14 +327,14 @@ def locate_debugtalk_py(start_path: Text) -> Text: return debugtalk_path -def locate_project_working_directory(test_path: Text) -> Tuple[Text, Text]: - """ locate debugtalk.py path as project working directory +def locate_project_root_directory(test_path: Text) -> Tuple[Text, Text]: + """ locate debugtalk.py path as project root directory Args: test_path: specified testfile path Returns: - (str, str): debugtalk.py path, project_working_directory + (str, str): debugtalk.py path, project_root_directory """ @@ -355,18 +355,18 @@ def locate_project_working_directory(test_path: Text) -> Tuple[Text, Text]: debugtalk_path = locate_debugtalk_py(test_path) if debugtalk_path: - # The folder contains debugtalk.py will be treated as PWD. - project_working_directory = os.path.dirname(debugtalk_path) + # The folder contains debugtalk.py will be treated as project RootDir. + project_root_directory = os.path.dirname(debugtalk_path) else: - # debugtalk.py not found, use os.getcwd() as PWD. - project_working_directory = os.getcwd() + # debugtalk.py not found, use os.getcwd() as project RootDir. + project_root_directory = os.getcwd() - return debugtalk_path, project_working_directory + return debugtalk_path, project_root_directory def load_debugtalk_functions() -> Dict[Text, Callable]: """ load project debugtalk.py module functions - debugtalk.py should be located in project working directory. + debugtalk.py should be located in project root directory. Returns: dict: debugtalk module functions mapping @@ -382,12 +382,12 @@ def load_debugtalk_functions() -> Dict[Text, Callable]: def load_project_meta(test_path: Text, reload: bool = False) -> ProjectMeta: - """ load api, testcases, .env, debugtalk.py functions. - api/testcases folder is relative to project_working_directory + """ 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. Args: - test_path (str): test file/folder path, locate pwd from this path. + test_path (str): test file/folder path, locate project RootDir from this path. reload: reload project meta if set true, default to false Returns: @@ -404,18 +404,16 @@ def load_project_meta(test_path: Text, reload: bool = False) -> ProjectMeta: if not test_path: return project_meta - debugtalk_path, project_working_directory = locate_project_working_directory( - test_path - ) + debugtalk_path, project_root_directory = locate_project_root_directory(test_path) - # add PWD to sys.path - sys.path.insert(0, project_working_directory) + # add project RootDir to sys.path + sys.path.insert(0, project_root_directory) # load .env file # NOTICE: # environment variable maybe loaded in debugtalk.py # thus .env file should be loaded before loading debugtalk.py - dot_env_path = os.path.join(project_working_directory, ".env") + dot_env_path = os.path.join(project_root_directory, ".env") dot_env = load_dot_env_file(dot_env_path) if dot_env: project_meta.env = dot_env @@ -427,8 +425,8 @@ def load_project_meta(test_path: Text, reload: bool = False) -> ProjectMeta: else: debugtalk_functions = {} - # locate PWD and load debugtalk.py functions - project_meta.PWD = project_working_directory + # locate project RootDir and load debugtalk.py functions + project_meta.RootDir = project_root_directory project_meta.functions = debugtalk_functions project_meta.debugtalk_path = debugtalk_path diff --git a/httprunner/make.py b/httprunner/make.py index 83d5eef8..d550faa4 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -64,7 +64,7 @@ def __ensure_absolute(path: Text) -> Text: if os.path.isabs(path): absolute_path = path else: - absolute_path = os.path.join(project_meta.PWD, path) + absolute_path = os.path.join(project_meta.RootDir, path) if not os.path.isfile(absolute_path): raise exceptions.ParamsError(f"Invalid testcase file path: {absolute_path}") diff --git a/httprunner/models.py b/httprunner/models.py index 59fbfcca..dcfd40f2 100644 --- a/httprunner/models.py +++ b/httprunner/models.py @@ -85,7 +85,7 @@ class ProjectMeta(BaseModel): dot_env_path: Text = "" # .env file path functions: FunctionsMapping = {} # functions defined in debugtalk.py env: Env = {} - PWD: Text = os.getcwd() # project working directory, the path debugtalk.py located + RootDir: Text = os.getcwd() # project root directory, the path debugtalk.py located class TestsMapping(BaseModel): diff --git a/httprunner/runner.py b/httprunner/runner.py index d135a8f7..c050deea 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -187,7 +187,9 @@ class HttpRunner(object): if os.path.isabs(step.testcase): ref_testcase_path = step.testcase else: - ref_testcase_path = os.path.join(self.__project_meta.PWD, step.testcase) + ref_testcase_path = os.path.join( + self.__project_meta.RootDir, step.testcase + ) case_result = ( HttpRunner() @@ -349,7 +351,7 @@ class HttpRunner(object): ) self.__case_id = self.__case_id or str(uuid.uuid4()) self.__log_path = self.__log_path or os.path.join( - self.__project_meta.PWD, "logs", f"{self.__case_id}.run.log" + self.__project_meta.RootDir, "logs", f"{self.__case_id}.run.log" ) log_handler = logger.add(self.__log_path, level="DEBUG") From d29941124d19fb897ffecd5f529577cf490685bd Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 6 Jun 2020 22:13:59 +0800 Subject: [PATCH 24/30] fix: ensure generated conftest.py located in generated pytest folder --- httprunner/compat.py | 7 ++++++- httprunner/make.py | 37 +------------------------------------ httprunner/utils.py | 38 +++++++++++++++++++++++++++++++++++++- tests/compat_test.py | 4 +--- tests/make_test.py | 29 ----------------------------- tests/utils_test.py | 29 +++++++++++++++++++++++++++++ 6 files changed, 74 insertions(+), 70 deletions(-) diff --git a/httprunner/compat.py b/httprunner/compat.py index dd001804..d4dd78d8 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -9,7 +9,7 @@ from loguru import logger from httprunner import exceptions from httprunner.loader import load_project_meta -from httprunner.utils import sort_dict_by_custom_order +from httprunner.utils import sort_dict_by_custom_order, ensure_file_path_valid def convert_jmespath(raw: Text) -> Text: @@ -221,6 +221,7 @@ def generate_conftest_for_summary(args: List): project_meta = load_project_meta(test_path) conftest_path = os.path.join(project_meta.RootDir, "conftest.py") + conftest_path = ensure_file_path_valid(conftest_path) if os.path.isfile(conftest_path): return @@ -308,6 +309,10 @@ def session_fixture(request): "{{SUMMARY_PATH_PLACEHOLDER}}", summary_path ) + dir_path = os.path.dirname(conftest_path) + if not os.path.exists(dir_path): + os.makedirs(dir_path) + with open(conftest_path, "w", encoding="utf-8") as f: f.write(conftest_content) diff --git a/httprunner/make.py b/httprunner/make.py index d550faa4..ede80460 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -1,5 +1,4 @@ import os -import string import subprocess from shutil import copyfile from typing import Text, List, Tuple, Dict, Set, NoReturn @@ -19,6 +18,7 @@ from httprunner.loader import ( ) from httprunner.parser import parse_data from httprunner.response import uniform_validator +from httprunner.utils import ensure_file_path_valid """ cache converted pytest files, avoid duplicate making """ @@ -123,41 +123,6 @@ def __ensure_project_meta_files(tests_path: Text) -> NoReturn: copyfile(dot_csv_path, dot_csv_new_path) -def ensure_file_path_valid(file_path: Text) -> Text: - """ ensure file path valid for pytest - - Args: - file_path: absolute or relative file path - - Returns: - ensured valid absolute file path - - """ - raw_file_name, file_suffix = os.path.splitext(file_path) - file_suffix = file_suffix.lower() - - if os.path.isabs(file_path): - raw_file_relative_name = raw_file_name[len(os.getcwd()) + 1 :] - else: - raw_file_relative_name = raw_file_name - - path_names = [] - for name in raw_file_relative_name.split(os.sep): - - if name[0] in string.digits: - # ensure file name not startswith digit - # 19 => T19, 2C => T2C - name = f"T{name}" - - # handle cases when directory name includes dot/hyphen/space - name = name.replace(" ", "_").replace(".", "_").replace("-", "_") - - path_names.append(name) - - new_file_path = os.path.join(os.getcwd(), f"{os.sep.join(path_names)}{file_suffix}") - return new_file_path - - def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: """convert single YAML/JSON testcase path to python file""" testcase_new_path = ensure_file_path_valid(testcase_path) diff --git a/httprunner/utils.py b/httprunner/utils.py index 3b206eea..07aa3ffc 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -2,8 +2,9 @@ import collections import json import os.path import platform +import string import uuid -from typing import Dict, List, Any +from typing import Dict, List, Any, Text import sentry_sdk from loguru import logger @@ -176,3 +177,38 @@ def sort_dict_by_custom_order(raw_dict: Dict, custom_order: List): return dict( sorted(raw_dict.items(), key=lambda i: get_index_from_list(custom_order, i[0])) ) + + +def ensure_file_path_valid(file_path: Text) -> Text: + """ ensure file path valid for pytest, handle cases when directory name includes dot/hyphen/space + + Args: + file_path: absolute or relative file path + + Returns: + ensured valid absolute file path + + """ + raw_file_name, file_suffix = os.path.splitext(file_path) + file_suffix = file_suffix.lower() + + if os.path.isabs(file_path): + raw_file_relative_name = raw_file_name[len(os.getcwd()) + 1 :] + else: + raw_file_relative_name = raw_file_name + + path_names = [] + for name in raw_file_relative_name.split(os.sep): + + if name[0] in string.digits: + # ensure file name not startswith digit + # 19 => T19, 2C => T2C + name = f"T{name}" + + # handle cases when directory name includes dot/hyphen/space + name = name.replace(" ", "_").replace(".", "_").replace("-", "_") + + path_names.append(name) + + new_file_path = os.path.join(os.getcwd(), f"{os.sep.join(path_names)}{file_suffix}") + return new_file_path diff --git a/tests/compat_test.py b/tests/compat_test.py index 04b55988..53a71baa 100644 --- a/tests/compat_test.py +++ b/tests/compat_test.py @@ -153,9 +153,7 @@ class TestCompat(unittest.TestCase): compat.ensure_cli_args(args2), ["examples/postman-echo/request.methods/hardcode.yml"], ) - self.assertTrue( - os.path.isfile("examples/postman_echo/request_methods/conftest.py") - ) + self.assertTrue(os.path.isfile("examples/postman_echo/conftest.py")) args3 = [ "examples/postman-echo/request.methods/hardcode.yml", diff --git a/tests/make_test.py b/tests/make_test.py index 0cb75282..122fbde6 100644 --- a/tests/make_test.py +++ b/tests/make_test.py @@ -8,7 +8,6 @@ from httprunner.make import ( make_config_chain_style, make_teststep_chain_style, pytest_files_run_set, - ensure_file_path_valid, ) @@ -67,34 +66,6 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( testcase_python_list, ) - def test_ensure_file_path_valid(self): - self.assertEqual( - ensure_file_path_valid( - "examples/postman-echo/request.methods/hardcode.yml" - ), - os.path.join( - os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" - ), - ) - self.assertEqual( - ensure_file_path_valid( - os.path.join(os.getcwd(), "postman-echo/request.methods/hardcode.yml") - ), - os.path.join(os.getcwd(), "postman_echo/request_methods/hardcode.yml"), - ) - self.assertEqual( - ensure_file_path_valid( - "examples/postman echo/request methods/hardcode.yml" - ), - os.path.join( - os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" - ), - ) - self.assertEqual( - ensure_file_path_valid("1/2B/3.yml"), - os.path.join(os.getcwd(), "T1/T2B/T3.yml"), - ) - def test_convert_testcase_path(self): self.assertEqual( convert_testcase_path("mubu.login.yml"), diff --git a/tests/utils_test.py b/tests/utils_test.py index cfb86e85..8a584054 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -2,6 +2,7 @@ import os import unittest from httprunner import loader, utils +from httprunner.utils import ensure_file_path_valid class TestUtils(unittest.TestCase): @@ -97,3 +98,31 @@ class TestUtils(unittest.TestCase): ), ["A", "D", "C", "B"], ) + + def test_ensure_file_path_valid(self): + self.assertEqual( + ensure_file_path_valid( + "examples/postman-echo/request.methods/hardcode.yml" + ), + os.path.join( + os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" + ), + ) + self.assertEqual( + ensure_file_path_valid( + os.path.join(os.getcwd(), "postman-echo/request.methods/hardcode.yml") + ), + os.path.join(os.getcwd(), "postman_echo/request_methods/hardcode.yml"), + ) + self.assertEqual( + ensure_file_path_valid( + "examples/postman echo/request methods/hardcode.yml" + ), + os.path.join( + os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" + ), + ) + self.assertEqual( + ensure_file_path_valid("1/2B/3.yml"), + os.path.join(os.getcwd(), "T1/T2B/T3.yml"), + ) From be627549e14b41d528c9aedf38a33745522c377b Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 6 Jun 2020 22:25:50 +0800 Subject: [PATCH 25/30] fix: ensure_file_path_valid for folder path --- httprunner/utils.py | 2 +- tests/utils_test.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/httprunner/utils.py b/httprunner/utils.py index 07aa3ffc..02d9b127 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -198,7 +198,7 @@ def ensure_file_path_valid(file_path: Text) -> Text: raw_file_relative_name = raw_file_name path_names = [] - for name in raw_file_relative_name.split(os.sep): + for name in raw_file_relative_name.rstrip(os.sep).split(os.sep): if name[0] in string.digits: # ensure file name not startswith digit diff --git a/tests/utils_test.py b/tests/utils_test.py index 8a584054..41514092 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -126,3 +126,7 @@ class TestUtils(unittest.TestCase): ensure_file_path_valid("1/2B/3.yml"), os.path.join(os.getcwd(), "T1/T2B/T3.yml"), ) + self.assertEqual( + ensure_file_path_valid("examples/postman-echo/request.methods/"), + os.path.join(os.getcwd(), "examples/postman_echo/request_methods"), + ) From 2697af09ac151945a3c120a9cc59c06881f05378 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 6 Jun 2020 22:34:39 +0800 Subject: [PATCH 26/30] fix: summary path --- httprunner/compat.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/httprunner/compat.py b/httprunner/compat.py index d4dd78d8..e683eaf5 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -220,11 +220,8 @@ def generate_conftest_for_summary(args: List): sys.exit(1) project_meta = load_project_meta(test_path) - conftest_path = os.path.join(project_meta.RootDir, "conftest.py") - conftest_path = ensure_file_path_valid(conftest_path) - if os.path.isfile(conftest_path): - return - + project_root_dir = ensure_file_path_valid(project_meta.RootDir) + conftest_path = os.path.join(project_root_dir, "conftest.py") conftest_content = '''# NOTICE: Generated By HttpRunner. import json import os @@ -292,8 +289,8 @@ def session_fixture(request): ''' test_path = os.path.abspath(test_path) - logs_dir_path = os.path.join(project_meta.RootDir, "logs") - test_path_relative_path = test_path[len(project_meta.RootDir) + 1 :] + logs_dir_path = os.path.join(project_root_dir, "logs") + test_path_relative_path = test_path[len(project_root_dir) + 1 :] if os.path.isdir(test_path): file_foder_path = os.path.join(logs_dir_path, test_path_relative_path) From f1fb78a6e6a48a179bfc079c8e58559a751532c6 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 7 Jun 2020 10:59:24 +0800 Subject: [PATCH 27/30] fix: clear project_meta before each test --- examples/postman_echo/conftest.py | 64 +++++++++++++++++++++++++++++++ tests/cli_test.py | 3 +- tests/make_test.py | 10 +++-- tests/runner_test.py | 2 + 4 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 examples/postman_echo/conftest.py diff --git a/examples/postman_echo/conftest.py b/examples/postman_echo/conftest.py new file mode 100644 index 00000000..b16b9c1e --- /dev/null +++ b/examples/postman_echo/conftest.py @@ -0,0 +1,64 @@ +# NOTICE: Generated By HttpRunner. +import json +import os +import time + +import pytest +from loguru import logger + +from httprunner.utils import get_platform + + +@pytest.fixture(scope="session", autouse=True) +def session_fixture(request): + """setup and teardown each task""" + logger.info(f"start running testcases ...") + + start_at = time.time() + + yield + + logger.info(f"task finished, generate task summary for --save-tests") + + summary = { + "success": True, + "stat": { + "testcases": {"total": 0, "success": 0, "fail": 0}, + "teststeps": {"total": 0, "failures": 0, "successes": 0}, + }, + "time": {"start_at": start_at, "duration": time.time() - start_at}, + "platform": get_platform(), + "details": [], + } + + for item in request.node.items: + testcase_summary = item.instance.get_summary() + summary["success"] &= testcase_summary.success + + summary["stat"]["testcases"]["total"] += 1 + summary["stat"]["teststeps"]["total"] += len(testcase_summary.step_datas) + if testcase_summary.success: + summary["stat"]["testcases"]["success"] += 1 + summary["stat"]["teststeps"]["successes"] += len( + testcase_summary.step_datas + ) + else: + summary["stat"]["testcases"]["fail"] += 1 + summary["stat"]["teststeps"]["successes"] += ( + len(testcase_summary.step_datas) - 1 + ) + summary["stat"]["teststeps"]["failures"] += 1 + + testcase_summary_json = testcase_summary.dict() + testcase_summary_json["records"] = testcase_summary_json.pop("step_datas") + summary["details"].append(testcase_summary_json) + + summary_path = "/Users/debugtalk/MyProjects/HttpRunner-dev/HttpRunner/examples/postman_echo/logs/request.methods/hardcode.summary.json" + summary_dir = os.path.dirname(summary_path) + os.makedirs(summary_dir, exist_ok=True) + + with open(summary_path, "w", encoding="utf-8") as f: + json.dump(summary, f, indent=4) + + logger.info(f"generated task summary: {summary_path}") + diff --git a/tests/cli_test.py b/tests/cli_test.py index 3ddd0efb..c84e294b 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -40,9 +40,10 @@ class TestCli(unittest.TestCase): self.assertIn(__description__, self.captured_output.getvalue().strip()) def test_debug_pytest(self): - pytest.main( + exit_code = pytest.main( [ "-s", "examples/postman_echo/request_methods/request_with_testcase_reference_test.py", ] ) + self.assertEqual(exit_code, 0) diff --git a/tests/make_test.py b/tests/make_test.py index 122fbde6..33de21d9 100644 --- a/tests/make_test.py +++ b/tests/make_test.py @@ -9,9 +9,15 @@ from httprunner.make import ( make_teststep_chain_style, pytest_files_run_set, ) +from httprunner import loader class TestMake(unittest.TestCase): + def setUp(self) -> None: + pytest_files_made_cache_mapping.clear() + pytest_files_run_set.clear() + loader.project_meta = None + def test_make_testcase(self): path = ["examples/postman-echo/request.methods/request_with_variables.yml"] testcase_python_list = main_make(path) @@ -27,8 +33,6 @@ class TestMake(unittest.TestCase): path = [ "examples/postman-echo/request.methods/request_with_testcase_reference.yml" ] - pytest_files_made_cache_mapping.clear() - pytest_files_run_set.clear() testcase_python_list = main_make(path) self.assertEqual(len(testcase_python_list), 1) self.assertIn( @@ -90,8 +94,6 @@ 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"] - pytest_files_made_cache_mapping.clear() - pytest_files_run_set.clear() testcase_python_list = main_make(path) self.assertEqual(len(testcase_python_list), 2) self.assertIn( diff --git a/tests/runner_test.py b/tests/runner_test.py index 66cef1c4..a63c0694 100644 --- a/tests/runner_test.py +++ b/tests/runner_test.py @@ -1,10 +1,12 @@ import unittest +from httprunner import loader from httprunner.runner import HttpRunner class TestHttpRunner(unittest.TestCase): def setUp(self): + loader.project_meta = None self.runner = HttpRunner() def test_run_testcase_by_path_request_only(self): From 534a14e525fd9b0f180f1ca6e4c143dd11101148 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 7 Jun 2020 11:10:12 +0800 Subject: [PATCH 28/30] test: update tests --- examples/postman-echo/debugtalk.py | 13 ---- .../__init__.py | 0 examples/postman_echo/conftest.py | 64 ------------------- .../cookie_manipulation/hardcode.yml | 0 .../set_delete_cookies.yml | 0 .../request_methods}/conftest.py | 0 .../request_methods}/demo_testsuite.yml | 4 +- .../request_with_functions_test.py | 2 +- .../request_with_testcase_reference_test.py | 2 +- .../request_methods}/hardcode.yml | 0 .../request_methods/hardcode_test.py | 2 +- .../request_with_functions.yml | 0 .../request_with_functions_test.py | 2 +- .../request_with_testcase_reference.yml | 2 +- .../request_with_testcase_reference_test.py | 2 +- .../request_with_variables.yml | 0 .../request_with_variables_test.py | 2 +- .../validate_with_functions.yml | 0 .../validate_with_functions_test.py | 2 +- .../validate_with_variables.yml | 0 .../validate_with_variables_test.py | 2 +- tests/compat_test.py | 16 ++--- tests/loader_test.py | 2 +- tests/make_test.py | 12 ++-- tests/runner_test.py | 4 +- tests/utils_test.py | 28 ++++---- 26 files changed, 39 insertions(+), 122 deletions(-) delete mode 100644 examples/postman-echo/debugtalk.py rename examples/{postman-echo => postman_echo}/__init__.py (100%) delete mode 100644 examples/postman_echo/conftest.py rename examples/{postman-echo => postman_echo}/cookie_manipulation/hardcode.yml (100%) rename examples/{postman-echo => postman_echo}/cookie_manipulation/set_delete_cookies.yml (100%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/conftest.py (100%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/demo_testsuite.yml (67%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/hardcode.yml (100%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/request_with_functions.yml (100%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/request_with_testcase_reference.yml (92%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/request_with_variables.yml (100%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/validate_with_functions.yml (100%) rename examples/{postman-echo/request.methods => postman_echo/request_methods}/validate_with_variables.yml (100%) diff --git a/examples/postman-echo/debugtalk.py b/examples/postman-echo/debugtalk.py deleted file mode 100644 index af8b22eb..00000000 --- a/examples/postman-echo/debugtalk.py +++ /dev/null @@ -1,13 +0,0 @@ -from httprunner import __version__ - - -def get_httprunner_version(): - return __version__ - - -def sum_two(m, n): - return m + n - - -def get_variables(): - return {"foo1": "session_bar1"} diff --git a/examples/postman-echo/__init__.py b/examples/postman_echo/__init__.py similarity index 100% rename from examples/postman-echo/__init__.py rename to examples/postman_echo/__init__.py diff --git a/examples/postman_echo/conftest.py b/examples/postman_echo/conftest.py deleted file mode 100644 index b16b9c1e..00000000 --- a/examples/postman_echo/conftest.py +++ /dev/null @@ -1,64 +0,0 @@ -# NOTICE: Generated By HttpRunner. -import json -import os -import time - -import pytest -from loguru import logger - -from httprunner.utils import get_platform - - -@pytest.fixture(scope="session", autouse=True) -def session_fixture(request): - """setup and teardown each task""" - logger.info(f"start running testcases ...") - - start_at = time.time() - - yield - - logger.info(f"task finished, generate task summary for --save-tests") - - summary = { - "success": True, - "stat": { - "testcases": {"total": 0, "success": 0, "fail": 0}, - "teststeps": {"total": 0, "failures": 0, "successes": 0}, - }, - "time": {"start_at": start_at, "duration": time.time() - start_at}, - "platform": get_platform(), - "details": [], - } - - for item in request.node.items: - testcase_summary = item.instance.get_summary() - summary["success"] &= testcase_summary.success - - summary["stat"]["testcases"]["total"] += 1 - summary["stat"]["teststeps"]["total"] += len(testcase_summary.step_datas) - if testcase_summary.success: - summary["stat"]["testcases"]["success"] += 1 - summary["stat"]["teststeps"]["successes"] += len( - testcase_summary.step_datas - ) - else: - summary["stat"]["testcases"]["fail"] += 1 - summary["stat"]["teststeps"]["successes"] += ( - len(testcase_summary.step_datas) - 1 - ) - summary["stat"]["teststeps"]["failures"] += 1 - - testcase_summary_json = testcase_summary.dict() - testcase_summary_json["records"] = testcase_summary_json.pop("step_datas") - summary["details"].append(testcase_summary_json) - - summary_path = "/Users/debugtalk/MyProjects/HttpRunner-dev/HttpRunner/examples/postman_echo/logs/request.methods/hardcode.summary.json" - summary_dir = os.path.dirname(summary_path) - os.makedirs(summary_dir, exist_ok=True) - - with open(summary_path, "w", encoding="utf-8") as f: - json.dump(summary, f, indent=4) - - logger.info(f"generated task summary: {summary_path}") - diff --git a/examples/postman-echo/cookie_manipulation/hardcode.yml b/examples/postman_echo/cookie_manipulation/hardcode.yml similarity index 100% rename from examples/postman-echo/cookie_manipulation/hardcode.yml rename to examples/postman_echo/cookie_manipulation/hardcode.yml diff --git a/examples/postman-echo/cookie_manipulation/set_delete_cookies.yml b/examples/postman_echo/cookie_manipulation/set_delete_cookies.yml similarity index 100% rename from examples/postman-echo/cookie_manipulation/set_delete_cookies.yml rename to examples/postman_echo/cookie_manipulation/set_delete_cookies.yml diff --git a/examples/postman-echo/request.methods/conftest.py b/examples/postman_echo/request_methods/conftest.py similarity index 100% rename from examples/postman-echo/request.methods/conftest.py rename to examples/postman_echo/request_methods/conftest.py diff --git a/examples/postman-echo/request.methods/demo_testsuite.yml b/examples/postman_echo/request_methods/demo_testsuite.yml similarity index 67% rename from examples/postman-echo/request.methods/demo_testsuite.yml rename to examples/postman_echo/request_methods/demo_testsuite.yml index d30490fc..946d37d7 100644 --- a/examples/postman-echo/request.methods/demo_testsuite.yml +++ b/examples/postman_echo/request_methods/demo_testsuite.yml @@ -5,11 +5,11 @@ config: testcases: - name: request with functions - testcase: request.methods/request_with_functions.yml + testcase: request_methods/request_with_functions.yml variables: var1: testsuite_val1 - name: request with referenced testcase - testcase: request.methods/request_with_testcase_reference.yml + testcase: request_methods/request_with_testcase_reference.yml variables: var2: testsuite_val2 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 f4cb1e6b..474e99c2 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 v3.0.9 -# FROM: examples/postman-echo/request.methods/request_with_functions.yml +# FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase 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 d968ae68..35a113d2 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 v3.0.9 -# FROM: examples/postman-echo/request.methods/request_with_testcase_reference.yml +# FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os import sys diff --git a/examples/postman-echo/request.methods/hardcode.yml b/examples/postman_echo/request_methods/hardcode.yml similarity index 100% rename from examples/postman-echo/request.methods/hardcode.yml rename to examples/postman_echo/request_methods/hardcode.yml diff --git a/examples/postman_echo/request_methods/hardcode_test.py b/examples/postman_echo/request_methods/hardcode_test.py index d296aac3..a499659b 100644 --- a/examples/postman_echo/request_methods/hardcode_test.py +++ b/examples/postman_echo/request_methods/hardcode_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman-echo/request.methods/hardcode.yml +# FROM: examples/postman_echo/request_methods/hardcode.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman-echo/request.methods/request_with_functions.yml b/examples/postman_echo/request_methods/request_with_functions.yml similarity index 100% rename from examples/postman-echo/request.methods/request_with_functions.yml rename to examples/postman_echo/request_methods/request_with_functions.yml diff --git a/examples/postman_echo/request_methods/request_with_functions_test.py b/examples/postman_echo/request_methods/request_with_functions_test.py index 2ba75345..340749f5 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman-echo/request.methods/request_with_functions.yml +# FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman-echo/request.methods/request_with_testcase_reference.yml b/examples/postman_echo/request_methods/request_with_testcase_reference.yml similarity index 92% rename from examples/postman-echo/request.methods/request_with_testcase_reference.yml rename to examples/postman_echo/request_methods/request_with_testcase_reference.yml index c66dd56d..37871f2c 100644 --- a/examples/postman-echo/request.methods/request_with_testcase_reference.yml +++ b/examples/postman_echo/request_methods/request_with_testcase_reference.yml @@ -10,7 +10,7 @@ teststeps: name: request with functions variables: foo1: override_bar1 - testcase: request.methods/request_with_functions.yml + testcase: request_methods/request_with_functions.yml export: - session_foo2 - diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py index 8c5b4b8e..75c211ff 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman-echo/request.methods/request_with_testcase_reference.yml +# FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os import sys diff --git a/examples/postman-echo/request.methods/request_with_variables.yml b/examples/postman_echo/request_methods/request_with_variables.yml similarity index 100% rename from examples/postman-echo/request.methods/request_with_variables.yml rename to examples/postman_echo/request_methods/request_with_variables.yml diff --git a/examples/postman_echo/request_methods/request_with_variables_test.py b/examples/postman_echo/request_methods/request_with_variables_test.py index f2f6ca44..47eff628 100644 --- a/examples/postman_echo/request_methods/request_with_variables_test.py +++ b/examples/postman_echo/request_methods/request_with_variables_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman-echo/request.methods/request_with_variables.yml +# FROM: examples/postman_echo/request_methods/request_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman-echo/request.methods/validate_with_functions.yml b/examples/postman_echo/request_methods/validate_with_functions.yml similarity index 100% rename from examples/postman-echo/request.methods/validate_with_functions.yml rename to examples/postman_echo/request_methods/validate_with_functions.yml diff --git a/examples/postman_echo/request_methods/validate_with_functions_test.py b/examples/postman_echo/request_methods/validate_with_functions_test.py index e2c049fd..b077ebc8 100644 --- a/examples/postman_echo/request_methods/validate_with_functions_test.py +++ b/examples/postman_echo/request_methods/validate_with_functions_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman-echo/request.methods/validate_with_functions.yml +# FROM: examples/postman_echo/request_methods/validate_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman-echo/request.methods/validate_with_variables.yml b/examples/postman_echo/request_methods/validate_with_variables.yml similarity index 100% rename from examples/postman-echo/request.methods/validate_with_variables.yml rename to examples/postman_echo/request_methods/validate_with_variables.yml diff --git a/examples/postman_echo/request_methods/validate_with_variables_test.py b/examples/postman_echo/request_methods/validate_with_variables_test.py index 72bd3aff..c333f462 100644 --- a/examples/postman_echo/request_methods/validate_with_variables_test.py +++ b/examples/postman_echo/request_methods/validate_with_variables_test.py @@ -1,5 +1,5 @@ # NOTICE: Generated By HttpRunner v3.0.9 -# FROM: examples/postman-echo/request.methods/validate_with_variables.yml +# FROM: examples/postman_echo/request_methods/validate_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/tests/compat_test.py b/tests/compat_test.py index 53a71baa..89707db7 100644 --- a/tests/compat_test.py +++ b/tests/compat_test.py @@ -142,28 +142,28 @@ class TestCompat(unittest.TestCase): ) def test_ensure_cli_args(self): - args1 = ["examples/postman-echo/request.methods/hardcode.yml", "--failfast"] + args1 = ["examples/postman_echo/request_methods/hardcode.yml", "--failfast"] self.assertEqual( compat.ensure_cli_args(args1), - ["examples/postman-echo/request.methods/hardcode.yml"], + ["examples/postman_echo/request_methods/hardcode.yml"], ) - args2 = ["examples/postman-echo/request.methods/hardcode.yml", "--save-tests"] + args2 = ["examples/postman_echo/request_methods/hardcode.yml", "--save-tests"] self.assertEqual( compat.ensure_cli_args(args2), - ["examples/postman-echo/request.methods/hardcode.yml"], + ["examples/postman_echo/request_methods/hardcode.yml"], ) self.assertTrue(os.path.isfile("examples/postman_echo/conftest.py")) args3 = [ - "examples/postman-echo/request.methods/hardcode.yml", + "examples/postman_echo/request_methods/hardcode.yml", "--report-file", "report.html", ] self.assertEqual( compat.ensure_cli_args(args3), [ - "examples/postman-echo/request.methods/hardcode.yml", + "examples/postman_echo/request_methods/hardcode.yml", "--html", "report.html", "--self-contained-html", @@ -171,7 +171,7 @@ class TestCompat(unittest.TestCase): ) args4 = [ - "examples/postman-echo/request.methods/hardcode.yml", + "examples/postman_echo/request_methods/hardcode.yml", "--failfast", "--save-tests", "--report-file", @@ -180,7 +180,7 @@ class TestCompat(unittest.TestCase): self.assertEqual( compat.ensure_cli_args(args4), [ - "examples/postman-echo/request.methods/hardcode.yml", + "examples/postman_echo/request_methods/hardcode.yml", "--html", "report.html", "--self-contained-html", diff --git a/tests/loader_test.py b/tests/loader_test.py index 27e216ec..c5e2ab36 100644 --- a/tests/loader_test.py +++ b/tests/loader_test.py @@ -6,7 +6,7 @@ from httprunner import exceptions, loader class TestLoader(unittest.TestCase): def test_load_testcase_file(self): - path = "examples/postman-echo/request.methods/request_with_variables.yml" + path = "examples/postman_echo/request_methods/request_with_variables.yml" testcase_obj = loader.load_testcase_file(path) self.assertEqual( testcase_obj.config.name, "request methods testcase with variables" diff --git a/tests/make_test.py b/tests/make_test.py index 33de21d9..12d8943d 100644 --- a/tests/make_test.py +++ b/tests/make_test.py @@ -19,7 +19,7 @@ class TestMake(unittest.TestCase): loader.project_meta = None def test_make_testcase(self): - path = ["examples/postman-echo/request.methods/request_with_variables.yml"] + path = ["examples/postman_echo/request_methods/request_with_variables.yml"] testcase_python_list = main_make(path) self.assertEqual( testcase_python_list[0], @@ -31,7 +31,7 @@ class TestMake(unittest.TestCase): def test_make_testcase_with_ref(self): path = [ - "examples/postman-echo/request.methods/request_with_testcase_reference.yml" + "examples/postman_echo/request_methods/request_with_testcase_reference.yml" ] testcase_python_list = main_make(path) self.assertEqual(len(testcase_python_list), 1) @@ -60,7 +60,7 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( ) def test_make_testcase_folder(self): - path = ["examples/postman-echo/request.methods/"] + path = ["examples/postman_echo/request_methods/"] testcase_python_list = main_make(path) self.assertIn( os.path.join( @@ -93,7 +93,7 @@ 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"] + path = ["examples/postman_echo/request_methods/demo_testsuite.yml"] testcase_python_list = main_make(path) self.assertEqual(len(testcase_python_list), 2) self.assertIn( @@ -115,13 +115,13 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( config = { "name": "request methods testcase: validate with functions", "variables": {"foo1": "bar1", "foo2": 22}, - "base_url": "https://postman-echo.com", + "base_url": "https://postman_echo.com", "verify": False, "path": "examples/postman_echo/request_methods/validate_with_functions_test.py", } self.assertEqual( make_config_chain_style(config), - """Config("request methods testcase: validate with functions").variables(**{'foo1': 'bar1', 'foo2': 22}).base_url("https://postman-echo.com").verify(False)""", + """Config("request methods testcase: validate with functions").variables(**{'foo1': 'bar1', 'foo2': 22}).base_url("https://postman_echo.com").verify(False)""", ) def test_make_teststep_chain_style(self): diff --git a/tests/runner_test.py b/tests/runner_test.py index a63c0694..7c664dde 100644 --- a/tests/runner_test.py +++ b/tests/runner_test.py @@ -11,7 +11,7 @@ class TestHttpRunner(unittest.TestCase): def test_run_testcase_by_path_request_only(self): self.runner.run_path( - "examples/postman-echo/request.methods/request_with_functions.yml" + "examples/postman_echo/request_methods/request_with_functions.yml" ) result = self.runner.get_summary() self.assertTrue(result.success) @@ -21,7 +21,7 @@ class TestHttpRunner(unittest.TestCase): def test_run_testcase_by_path_ref_testcase(self): self.runner.run_path( - "examples/postman-echo/request.methods/request_with_testcase_reference.yml" + "examples/postman_echo/request_methods/request_with_testcase_reference.yml" ) result = self.runner.get_summary() self.assertTrue(result.success) diff --git a/tests/utils_test.py b/tests/utils_test.py index 41514092..2a01c08a 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -102,24 +102,10 @@ class TestUtils(unittest.TestCase): def test_ensure_file_path_valid(self): self.assertEqual( ensure_file_path_valid( - "examples/postman-echo/request.methods/hardcode.yml" + "examples/a-b.c/d f/hardcode.yml" ), os.path.join( - os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" - ), - ) - self.assertEqual( - ensure_file_path_valid( - os.path.join(os.getcwd(), "postman-echo/request.methods/hardcode.yml") - ), - os.path.join(os.getcwd(), "postman_echo/request_methods/hardcode.yml"), - ) - self.assertEqual( - ensure_file_path_valid( - "examples/postman echo/request methods/hardcode.yml" - ), - os.path.join( - os.getcwd(), "examples/postman_echo/request_methods/hardcode.yml" + os.getcwd(), "examples/a_b_c/d_f/hardcode.yml" ), ) self.assertEqual( @@ -127,6 +113,14 @@ class TestUtils(unittest.TestCase): os.path.join(os.getcwd(), "T1/T2B/T3.yml"), ) self.assertEqual( - ensure_file_path_valid("examples/postman-echo/request.methods/"), + ensure_file_path_valid( + "examples/a-b.c/2B/hardcode.yml" + ), + os.path.join( + os.getcwd(), "examples/a_b_c/T2B/hardcode.yml" + ), + ) + self.assertEqual( + ensure_file_path_valid("examples/postman_echo/request_methods/"), os.path.join(os.getcwd(), "examples/postman_echo/request_methods"), ) From 204b013b7114a1edd00cf089632a0c1c5bb42dd7 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 7 Jun 2020 12:43:01 +0800 Subject: [PATCH 29/30] test: add unittest form testcase with abnormal path --- docs/CHANGELOG.md | 2 +- examples/httpbin/basic_test.py | 2 +- examples/httpbin/hooks_test.py | 2 +- examples/httpbin/load_image_test.py | 2 +- examples/httpbin/upload_test.py | 2 +- examples/httpbin/validate_test.py | 2 +- .../request_with_functions_test.py | 2 +- .../request_with_testcase_reference_test.py | 2 +- .../request_methods/hardcode_test.py | 2 +- .../request_with_functions_test.py | 2 +- .../request_with_testcase_reference_test.py | 2 +- .../request_with_variables_test.py | 2 +- .../validate_with_functions_test.py | 2 +- .../validate_with_variables_test.py | 2 +- httprunner/cli.py | 7 +++-- httprunner/make.py | 2 +- tests/data/a-b.c/1.yml | 30 +++++++++++++++++++ tests/data/a-b.c/2 3.yml | 30 +++++++++++++++++++ tests/data/a-b.c/__init__.py | 0 tests/data/a-b.c/debugtalk.py | 13 ++++++++ tests/runner_test.py | 10 +++++++ tests/utils_test.py | 16 +++------- 22 files changed, 106 insertions(+), 30 deletions(-) create mode 100644 tests/data/a-b.c/1.yml create mode 100644 tests/data/a-b.c/2 3.yml create mode 100644 tests/data/a-b.c/__init__.py create mode 100644 tests/data/a-b.c/debugtalk.py diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d6f55e08..10e819d7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 3.0.9 (2020-06-05) +## 3.0.9 (2020-06-07) **Fixed** diff --git a/examples/httpbin/basic_test.py b/examples/httpbin/basic_test.py index b74d7c1d..bfa47499 100644 --- a/examples/httpbin/basic_test.py +++ b/examples/httpbin/basic_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/basic.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/hooks_test.py b/examples/httpbin/hooks_test.py index aeb92dd3..cec6ad42 100644 --- a/examples/httpbin/hooks_test.py +++ b/examples/httpbin/hooks_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/hooks.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/load_image_test.py b/examples/httpbin/load_image_test.py index 16a9044c..cca17a7c 100644 --- a/examples/httpbin/load_image_test.py +++ b/examples/httpbin/load_image_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/load_image.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/upload_test.py b/examples/httpbin/upload_test.py index d89d30f9..6ed390a8 100644 --- a/examples/httpbin/upload_test.py +++ b/examples/httpbin/upload_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/upload.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/validate_test.py b/examples/httpbin/validate_test.py index 8e7d8618..093d5075 100644 --- a/examples/httpbin/validate_test.py +++ b/examples/httpbin/validate_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/httpbin/validate.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase 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 474e99c2..acb70cd0 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,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase 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 35a113d2..294df37f 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,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os diff --git a/examples/postman_echo/request_methods/hardcode_test.py b/examples/postman_echo/request_methods/hardcode_test.py index a499659b..ffa7d91a 100644 --- a/examples/postman_echo/request_methods/hardcode_test.py +++ b/examples/postman_echo/request_methods/hardcode_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/hardcode.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_functions_test.py b/examples/postman_echo/request_methods/request_with_functions_test.py index 340749f5..159d93b6 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py index 75c211ff..ece17405 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os diff --git a/examples/postman_echo/request_methods/request_with_variables_test.py b/examples/postman_echo/request_methods/request_with_variables_test.py index 47eff628..aab91e97 100644 --- a/examples/postman_echo/request_methods/request_with_variables_test.py +++ b/examples/postman_echo/request_methods/request_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/request_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/validate_with_functions_test.py b/examples/postman_echo/request_methods/validate_with_functions_test.py index b077ebc8..5a730f1e 100644 --- a/examples/postman_echo/request_methods/validate_with_functions_test.py +++ b/examples/postman_echo/request_methods/validate_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/validate_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/validate_with_variables_test.py b/examples/postman_echo/request_methods/validate_with_variables_test.py index c333f462..26c2467f 100644 --- a/examples/postman_echo/request_methods/validate_with_variables_test.py +++ b/examples/postman_echo/request_methods/validate_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTICE: Generated By HttpRunner v3.0.9 +# NOTE: Generated By HttpRunner v3.0.9 # FROM: examples/postman_echo/request_methods/validate_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/httprunner/cli.py b/httprunner/cli.py index 7786f43e..c7f89910 100644 --- a/httprunner/cli.py +++ b/httprunner/cli.py @@ -1,4 +1,5 @@ import argparse +import enum import os import sys @@ -23,7 +24,7 @@ def init_parser_run(subparsers): return sub_parser_run -def main_run(extra_args): +def main_run(extra_args) -> enum.IntEnum: capture_message("start to run") # keep compatibility with v2 extra_args = ensure_cli_args(extra_args) @@ -52,7 +53,7 @@ def main_run(extra_args): extra_args_new.append("--tb=short") extra_args_new.extend(testcase_path_list) - sys.exit(pytest.main(extra_args_new)) + return pytest.main(extra_args_new) def main(): @@ -112,7 +113,7 @@ def main(): sys.exit(0) if sys.argv[1] == "run": - main_run(extra_args) + sys.exit(main_run(extra_args)) elif sys.argv[1] == "startproject": main_scaffold(args) elif sys.argv[1] == "har2case": diff --git a/httprunner/make.py b/httprunner/make.py index ede80460..5ddc22b6 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -29,7 +29,7 @@ pytest_files_made_cache_mapping: Dict[Text, Text] = {} pytest_files_run_set: Set = set() __TEMPLATE__ = jinja2.Template( - """# NOTICE: Generated By HttpRunner v{{ version }} + """# NOTE: Generated By HttpRunner v{{ version }} # FROM: {{ testcase_path }} {% if imports_list %} import os diff --git a/tests/data/a-b.c/1.yml b/tests/data/a-b.c/1.yml new file mode 100644 index 00000000..ffb63d09 --- /dev/null +++ b/tests/data/a-b.c/1.yml @@ -0,0 +1,30 @@ +config: + name: "request methods testcase with functions" + variables: + foo1: session_bar1 + base_url: "https://postman-echo.com" + verify: False + +teststeps: +- + name: get with params + variables: + foo1: bar1 + foo2: session_bar2 + sum_v: "${sum_two(1, 2)}" + request: + method: GET + url: /get + params: + foo1: $foo1 + foo2: $foo2 + sum_v: $sum_v + headers: + User-Agent: HttpRunner/${get_httprunner_version()} + extract: + session_foo2: "body.args.foo2" + validate: + - eq: ["status_code", 200] + - eq: ["body.args.foo1", "session_bar1"] + - eq: ["body.args.sum_v", "3"] + - eq: ["body.args.foo2", "session_bar2"] diff --git a/tests/data/a-b.c/2 3.yml b/tests/data/a-b.c/2 3.yml new file mode 100644 index 00000000..ef4961ed --- /dev/null +++ b/tests/data/a-b.c/2 3.yml @@ -0,0 +1,30 @@ +config: + name: "reference testcase unittest for abnormal folder path" + variables: + foo1: session_bar1 + base_url: "https://postman-echo.com" + verify: False + +teststeps: +- + name: request with functions + variables: + foo1: override_bar1 + testcase: 1.yml + export: + - session_foo2 +- + name: post form data + variables: + foo1: bar1 + request: + method: POST + url: /post + headers: + User-Agent: HttpRunner/${get_httprunner_version()} + Content-Type: "application/x-www-form-urlencoded" + data: "foo1=$foo1&foo2=$session_foo2" + validate: + - eq: ["status_code", 200] + - eq: ["body.form.foo1", "session_bar1"] + - eq: ["body.form.foo2", "session_bar2"] diff --git a/tests/data/a-b.c/__init__.py b/tests/data/a-b.c/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/a-b.c/debugtalk.py b/tests/data/a-b.c/debugtalk.py new file mode 100644 index 00000000..af8b22eb --- /dev/null +++ b/tests/data/a-b.c/debugtalk.py @@ -0,0 +1,13 @@ +from httprunner import __version__ + + +def get_httprunner_version(): + return __version__ + + +def sum_two(m, n): + return m + n + + +def get_variables(): + return {"foo1": "session_bar1"} diff --git a/tests/runner_test.py b/tests/runner_test.py index 7c664dde..b91fd3df 100644 --- a/tests/runner_test.py +++ b/tests/runner_test.py @@ -1,6 +1,8 @@ +import os import unittest from httprunner import loader +from httprunner.cli import main_run from httprunner.runner import HttpRunner @@ -28,3 +30,11 @@ class TestHttpRunner(unittest.TestCase): self.assertEqual(result.name, "request methods testcase: reference testcase") self.assertEqual(result.step_datas[0].name, "request with functions") self.assertEqual(len(result.step_datas), 2) + + def test_run_testcase_with_abnormal_path(self): + exit_code = main_run(["tests/data/a-b.c/2 3.yml"]) + self.assertEqual(exit_code, 0) + self.assertTrue(os.path.exists("tests/data/a_b_c/__init__.py")) + self.assertTrue(os.path.exists("tests/data/a_b_c/debugtalk.py")) + self.assertTrue(os.path.exists("tests/data/a_b_c/T1_test.py")) + self.assertTrue(os.path.exists("tests/data/a_b_c/T2_3_test.py")) diff --git a/tests/utils_test.py b/tests/utils_test.py index 2a01c08a..b15a5c7f 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -101,24 +101,16 @@ class TestUtils(unittest.TestCase): def test_ensure_file_path_valid(self): self.assertEqual( - ensure_file_path_valid( - "examples/a-b.c/d f/hardcode.yml" - ), - os.path.join( - os.getcwd(), "examples/a_b_c/d_f/hardcode.yml" - ), + ensure_file_path_valid("examples/a-b.c/d f/hardcode.yml"), + os.path.join(os.getcwd(), "examples/a_b_c/d_f/hardcode.yml"), ) self.assertEqual( ensure_file_path_valid("1/2B/3.yml"), os.path.join(os.getcwd(), "T1/T2B/T3.yml"), ) self.assertEqual( - ensure_file_path_valid( - "examples/a-b.c/2B/hardcode.yml" - ), - os.path.join( - os.getcwd(), "examples/a_b_c/T2B/hardcode.yml" - ), + ensure_file_path_valid("examples/a-b.c/2B/hardcode.yml"), + os.path.join(os.getcwd(), "examples/a_b_c/T2B/hardcode.yml"), ) self.assertEqual( ensure_file_path_valid("examples/postman_echo/request_methods/"), From b279162956248cf9d4a6e6cec03c2fc683380ba1 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 7 Jun 2020 12:51:45 +0800 Subject: [PATCH 30/30] change: search debugtalk.py upward recursively until system root dir --- docs/CHANGELOG.md | 1 + httprunner/loader.py | 9 +-------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 10e819d7..e7a287da 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,7 @@ - change: rename TestCaseInOut field, config_vars and export_vars - change: rename StepData field, export_vars - change: add `--tb=short` for `hrun` command to use shorter traceback format by default +- change: search debugtalk.py upward recursively until system root dir ## 3.0.8 (2020-06-04) diff --git a/httprunner/loader.py b/httprunner/loader.py index ff37c15c..4982eb92 100644 --- a/httprunner/loader.py +++ b/httprunner/loader.py @@ -265,7 +265,7 @@ def load_builtin_functions() -> Dict[Text, Callable]: def locate_file(start_path: Text, file_name: Text) -> Text: """ locate filename and return absolute file path. - searching will be recursive upward until current working directory or system root dir. + searching will be recursive upward until system root dir. Args: file_name (str): target locate file name @@ -289,13 +289,6 @@ def locate_file(start_path: Text, file_name: Text) -> Text: if os.path.isfile(file_path): return os.path.abspath(file_path) - # current working directory - cwd = os.getcwd() - if os.path.abspath(start_dir_path) == cwd: - raise exceptions.FileNotFound( - f"{file_name} not found for {start_path}\ncurrent working directory: {cwd}" - ) - # system root dir # Windows, e.g. 'E:\\' # Linux/Darwin, '/'