diff --git a/examples/postman_echo/request_methods/conftest.py b/examples/postman_echo/request_methods/conftest.py index 4f0e444c..788c2686 100644 --- a/examples/postman_echo/request_methods/conftest.py +++ b/examples/postman_echo/request_methods/conftest.py @@ -2,10 +2,9 @@ import uuid from typing import List import pytest +from httprunner import Config, Step from loguru import logger -from httprunner.schema import TConfig, TStep - @pytest.fixture(scope="session", autouse=True) def session_fixture(request): @@ -33,8 +32,8 @@ def session_fixture(request): @pytest.fixture(scope="function", autouse=True) def testcase_fixture(request): """setup and teardown each testcase""" - config: TConfig = request.cls.config - teststeps: List[TStep] = request.cls.teststeps + config: Config = request.cls.config + teststeps: List[Step] = request.cls.teststeps logger.debug(f"setup testcase fixture: {config.name} - {request.module.__name__}") 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 c8fad6a6..49031e9a 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -10,10 +10,9 @@ class TestCaseRequestWithFunctions(HttpRunner): .variables(foo1="session_bar1") .base_url("https://postman-echo.com") .verify(False) - .path( + .set_path( "examples/postman_echo/request_methods/request_with_functions_test.py" ) - .init() ) teststeps = [ @@ -29,8 +28,7 @@ class TestCaseRequestWithFunctions(HttpRunner): .assert_equal("status_code", 200) .assert_equal("body.args.foo1", "session_bar1") .assert_equal("body.args.sum_v", "3") - .assert_equal("body.args.foo2", "session_bar2") - .init(), + .assert_equal("body.args.foo2", "session_bar2"), Step("post raw text") .with_variables(foo1="hello world", foo3="$session_foo2") .run_request( @@ -50,8 +48,7 @@ class TestCaseRequestWithFunctions(HttpRunner): .assert_equal( "body.data", "This is expected to be sent back as part of response body: session_bar1-session_bar2.", - ) - .init(), + ), Step("post form data") .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) .run_request( @@ -67,8 +64,7 @@ class TestCaseRequestWithFunctions(HttpRunner): ) .assert_equal("status_code", 200) .assert_equal("body.form.foo1", "session_bar1") - .assert_equal("body.form.foo2", "bar2") - .init(), + .assert_equal("body.form.foo2", "bar2"), ] diff --git a/httprunner/runner.py b/httprunner/runner.py index 20663408..c4101321 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -20,6 +20,7 @@ from httprunner.ext.uploader import prepare_upload_step 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 ( TConfig, TStep, @@ -34,10 +35,12 @@ from httprunner.schema import ( class HttpRunner(object): - config: TConfig - teststeps: List[TStep] + config: Config + teststeps: List[Step] success: bool = True # indicate testcase execution result + __config: TConfig + __teststeps: List[TStep] __project_meta: ProjectMeta = None __case_id: Text = "" __step_datas: List[StepData] = None @@ -84,7 +87,7 @@ class HttpRunner(object): # prepare arguments method = parsed_request_dict.pop("method") url_path = parsed_request_dict.pop("url") - url = build_url(self.config.base_url, url_path) + url = build_url(self.__config.base_url, url_path) parsed_request_dict["json"] = parsed_request_dict.pop("req_json", {}) # request @@ -220,21 +223,23 @@ class HttpRunner(object): >>> HttpRunner().with_project_meta(project_meta).run_testcase(testcase_obj) """ - self.config = testcase.config - self.teststeps = testcase.teststeps + self.__config = testcase.config + self.__teststeps = testcase.teststeps # prepare - self.__project_meta = self.__project_meta or load_project_meta(self.config.path) - self.__parse_config(self.config) + self.__project_meta = self.__project_meta or load_project_meta( + self.__config.path + ) + self.__parse_config(self.__config) self.__start_at = time.time() self.__step_datas: List[StepData] = [] self.__session = self.__session or HttpSession() self.__session_variables = {} # run teststeps - for step in self.teststeps: + for step in self.__teststeps: # update with config variables - step.variables.update(self.config.variables) + step.variables.update(self.__config.variables) # update with session variables extracted from pre step step.variables.update(self.__session_variables) # parse variables @@ -267,7 +272,9 @@ class HttpRunner(object): >>> TestCaseRequestWithFunctions().run() """ - testcase_obj = TestCase(config=self.config, teststeps=self.teststeps) + self.__config = self.config.perform() + self.__teststeps = [step.perform() for step in self.teststeps] + testcase_obj = TestCase(config=self.__config, teststeps=self.__teststeps) return self.run_testcase(testcase_obj) def get_step_datas(self) -> List[StepData]: @@ -275,7 +282,7 @@ class HttpRunner(object): def get_export_variables(self) -> Dict: export_vars_mapping = {} - for var_name in self.config.export: + for var_name in self.__config.export: if var_name not in self.__session_variables: raise ParamsError( f"failed to export variable {var_name} from session variables {self.__session_variables}" @@ -290,7 +297,7 @@ class HttpRunner(object): start_at_timestamp = self.__start_at start_at_iso_format = datetime.utcfromtimestamp(start_at_timestamp).isoformat() return TestCaseSummary( - name=self.config.name, + name=self.__config.name, success=self.success, case_id=self.__case_id, time=TestCaseTime( @@ -299,7 +306,7 @@ class HttpRunner(object): duration=self.__duration, ), in_out=TestCaseInOut( - vars=self.config.variables, export=self.get_export_variables() + vars=self.__config.variables, export=self.get_export_variables() ), log=self.__log_path, step_datas=self.__step_datas, @@ -307,7 +314,11 @@ class HttpRunner(object): def test_start(self) -> "HttpRunner": """main entrance, discovered by pytest""" - self.__project_meta = self.__project_meta or load_project_meta(self.config.path) + self.__config = self.config.perform() + self.__teststeps = [step.perform() for step in self.teststeps] + self.__project_meta = self.__project_meta or load_project_meta( + self.__config.path + ) 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" @@ -315,24 +326,24 @@ class HttpRunner(object): log_handler = logger.add(self.__log_path, level="DEBUG") # parse config name - variables = self.config.variables + variables = self.__config.variables variables.update(self.__session_variables) - self.config.name = parse_data( - self.config.name, variables, self.__project_meta.functions + self.__config.name = parse_data( + self.__config.name, variables, self.__project_meta.functions ) if USE_ALLURE: # update allure report meta - allure.dynamic.title(self.config.name) + allure.dynamic.title(self.__config.name) allure.dynamic.description(f"TestCase ID: {self.__case_id}") logger.info( - f"Start to run testcase: {self.config.name}, TestCase ID: {self.__case_id}" + f"Start to run testcase: {self.__config.name}, TestCase ID: {self.__case_id}" ) try: return self.run_testcase( - TestCase(config=self.config, teststeps=self.teststeps) + TestCase(config=self.__config, teststeps=self.__teststeps) ) finally: logger.remove(log_handler) diff --git a/httprunner/testcase.py b/httprunner/testcase.py index dafd4f67..0ed30d4d 100644 --- a/httprunner/testcase.py +++ b/httprunner/testcase.py @@ -1,4 +1,4 @@ -from typing import Text, Any +from typing import Text, Any, Dict from httprunner.schema import ( TConfig, @@ -16,6 +16,14 @@ class Config(object): self.__verify = False self.__path = "" + @property + def name(self): + return self.__name + + @property + def path(self): + return self.__path + def variables(self, **variables) -> "Config": self.__variables.update(variables) return self @@ -28,11 +36,11 @@ class Config(object): self.__verify = verify return self - def path(self, path: Text) -> "Config": + def set_path(self, path: Text) -> "Config": self.__path = path return self - def init(self) -> TConfig: + def perform(self) -> TConfig: return TConfig( name=self.__name, base_url=self.__base_url, @@ -42,7 +50,7 @@ class Config(object): ) -class RequestOptionalArgs(object): +class RequestWithOptionalArgs(object): def __init__(self, method: MethodEnum, url: Text): self.__method = method self.__url = url @@ -54,31 +62,31 @@ class RequestOptionalArgs(object): self.__allow_redirects = True self.__verify = False - def with_params(self, **params) -> "RequestOptionalArgs": + def with_params(self, **params) -> "RequestWithOptionalArgs": self.__params.update(params) return self - def with_headers(self, **headers) -> "RequestOptionalArgs": + def with_headers(self, **headers) -> "RequestWithOptionalArgs": self.__headers.update(headers) return self - def with_cookies(self, **cookies) -> "RequestOptionalArgs": + def with_cookies(self, **cookies) -> "RequestWithOptionalArgs": self.__cookies.update(cookies) return self - def with_data(self, data) -> "RequestOptionalArgs": + def with_data(self, data) -> "RequestWithOptionalArgs": self.__data = data return self - def set_timeout(self, timeout: float) -> "RequestOptionalArgs": + def set_timeout(self, timeout: float) -> "RequestWithOptionalArgs": self.__timeout = timeout return self - def set_verify(self, verify: bool) -> "RequestOptionalArgs": + def set_verify(self, verify: bool) -> "RequestWithOptionalArgs": self.__verify = verify return self - def set_allow_redirects(self, allow_redirects: bool) -> "RequestOptionalArgs": + def set_allow_redirects(self, allow_redirects: bool) -> "RequestWithOptionalArgs": self.__allow_redirects = allow_redirects return self @@ -97,30 +105,30 @@ class RequestOptionalArgs(object): class Request(object): - def get(self, url: Text) -> RequestOptionalArgs: - return RequestOptionalArgs(MethodEnum.GET, url) + def get(self, url: Text) -> RequestWithOptionalArgs: + return RequestWithOptionalArgs(MethodEnum.GET, url) - def post(self, url: Text) -> RequestOptionalArgs: - return RequestOptionalArgs(MethodEnum.POST, url) + def post(self, url: Text) -> RequestWithOptionalArgs: + return RequestWithOptionalArgs(MethodEnum.POST, url) - def put(self, url: Text) -> RequestOptionalArgs: - return RequestOptionalArgs(MethodEnum.PUT, url) + def put(self, url: Text) -> RequestWithOptionalArgs: + return RequestWithOptionalArgs(MethodEnum.PUT, url) - def head(self, url: Text) -> RequestOptionalArgs: - return RequestOptionalArgs(MethodEnum.HEAD, url) + def head(self, url: Text) -> RequestWithOptionalArgs: + return RequestWithOptionalArgs(MethodEnum.HEAD, url) - def delete(self, url: Text) -> RequestOptionalArgs: - return RequestOptionalArgs(MethodEnum.DELETE, url) + def delete(self, url: Text) -> RequestWithOptionalArgs: + return RequestWithOptionalArgs(MethodEnum.DELETE, url) - def options(self, url: Text) -> RequestOptionalArgs: - return RequestOptionalArgs(MethodEnum.OPTIONS, url) + def options(self, url: Text) -> RequestWithOptionalArgs: + return RequestWithOptionalArgs(MethodEnum.OPTIONS, url) - def patch(self, url: Text) -> RequestOptionalArgs: - return RequestOptionalArgs(MethodEnum.PATCH, url) + def patch(self, url: Text) -> RequestWithOptionalArgs: + return RequestWithOptionalArgs(MethodEnum.PATCH, url) class Step(object): - def __init__(self, name): + def __init__(self, name: Text): self.__name = name self.__variables = {} self.__request = None @@ -131,7 +139,11 @@ class Step(object): self.__variables.update(variables) return self - def run_request(self, req_obj: RequestOptionalArgs) -> "Step": + @property + def request(self) -> TRequest: + return self.__request + + def run_request(self, req_obj: RequestWithOptionalArgs) -> "Step": self.__request = req_obj.perform() return self @@ -151,7 +163,7 @@ class Step(object): self.__validators.append({"lt": [jmes_path, expected_value]}) return self - def init(self) -> TStep: + def perform(self) -> TStep: return TStep( name=self.__name, variables=self.__variables,