refactor: HttpRunner config & teststeps

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