mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 07:26:55 +08:00
refactor: HttpRunner config & teststeps
This commit is contained in:
@@ -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
@@ -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
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user