mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
refactor: HttpRunner config & teststeps
This commit is contained in:
@@ -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__}")
|
||||
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user