mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
186 lines
6.3 KiB
Python
186 lines
6.3 KiB
Python
import os
|
|
from typing import List, Dict
|
|
|
|
from loguru import logger
|
|
|
|
from httprunner import utils, exceptions
|
|
from httprunner.client import HttpSession
|
|
from httprunner.exceptions import ValidationFailure, ParamsError
|
|
from httprunner.new_loader import load_project_data, load_testcase_file
|
|
from httprunner.parser import build_url, parse_data, parse_variables_mapping
|
|
from httprunner.response import ResponseObject
|
|
from httprunner.schema import (
|
|
TConfig,
|
|
TStep,
|
|
VariablesMapping,
|
|
StepData,
|
|
)
|
|
|
|
|
|
class HttpRunner(object):
|
|
def __init__(
|
|
self,
|
|
config: TConfig,
|
|
teststeps: List[TStep],
|
|
session: HttpSession = None,
|
|
):
|
|
if not config.path:
|
|
raise exceptions.ParamsError("config path missed!")
|
|
|
|
self.config = config
|
|
self.teststeps = teststeps
|
|
self.session = session
|
|
self.step_datas: List[StepData] = []
|
|
self.validation_results: Dict = {}
|
|
self.session_variables: Dict = {}
|
|
self.success: bool = True # indicate testcase execution result
|
|
|
|
self.project_data = load_project_data(self.config.path)
|
|
self.config.functions = self.project_data.functions
|
|
|
|
def with_variables(self, **variables: VariablesMapping) -> "HttpRunner":
|
|
self.config.variables.update(variables)
|
|
return self
|
|
|
|
def __run_step_request(self, step: TStep):
|
|
"""run teststep: request"""
|
|
step_data = StepData(name=step.name)
|
|
|
|
# parse
|
|
request_dict = step.request.dict()
|
|
parsed_request_dict = parse_data(
|
|
request_dict, step.variables, self.config.functions
|
|
)
|
|
|
|
# prepare arguments
|
|
method = parsed_request_dict.pop("method")
|
|
url_path = parsed_request_dict.pop("url")
|
|
url = build_url(self.config.base_url, url_path)
|
|
|
|
parsed_request_dict["json"] = parsed_request_dict.pop("req_json", {})
|
|
|
|
logger.info(f"{method} {url}")
|
|
logger.debug(f"request kwargs(raw): {parsed_request_dict}")
|
|
|
|
# request
|
|
self.session = self.session or HttpSession()
|
|
resp = self.session.request(method, url, **parsed_request_dict)
|
|
resp_obj = ResponseObject(resp)
|
|
|
|
def log_req_resp_details():
|
|
err_msg = "\n{} DETAILED REQUEST & RESPONSE {}\n".format("*" * 32, "*" * 32)
|
|
|
|
# log request
|
|
err_msg += "====== request details ======\n"
|
|
err_msg += f"url: {url}\n"
|
|
err_msg += f"method: {method}\n"
|
|
headers = parsed_request_dict.pop("headers", {})
|
|
err_msg += f"headers: {headers}\n"
|
|
for k, v in parsed_request_dict.items():
|
|
v = utils.omit_long_data(v)
|
|
err_msg += f"{k}: {repr(v)}\n"
|
|
|
|
err_msg += "\n"
|
|
|
|
# log response
|
|
err_msg += "====== response details ======\n"
|
|
err_msg += f"status_code: {resp.status_code}\n"
|
|
err_msg += f"headers: {resp.headers}\n"
|
|
err_msg += f"body: {repr(resp.text)}\n"
|
|
logger.error(err_msg)
|
|
|
|
# extract
|
|
extractors = step.extract
|
|
extract_mapping = resp_obj.extract(extractors)
|
|
step_data.export = extract_mapping
|
|
|
|
variables_mapping = step.variables
|
|
variables_mapping.update(extract_mapping)
|
|
|
|
# validate
|
|
validators = step.validators
|
|
try:
|
|
resp_obj.validate(validators, variables_mapping, self.config.functions)
|
|
self.session.data.success = True
|
|
except ValidationFailure:
|
|
self.session.data.success = False
|
|
log_req_resp_details()
|
|
raise
|
|
finally:
|
|
self.validation_results = resp_obj.validation_results
|
|
# save request & response meta data
|
|
self.session.data.validators = self.validation_results
|
|
self.success &= self.session.data.success
|
|
|
|
step_data.success = self.session.data.success
|
|
step_data.data = self.session.data
|
|
return step_data
|
|
|
|
def __run_step_testcase(self, step):
|
|
"""run teststep: referenced testcase"""
|
|
step_data = StepData(name=step.name)
|
|
step_variables = step.variables
|
|
|
|
ref_testcase_path = os.path.join(self.project_data.PWD, step.testcase)
|
|
_, testcase_obj = load_testcase_file(ref_testcase_path)
|
|
|
|
case_result = (
|
|
HttpRunner(testcase_obj.config, testcase_obj.teststeps, self.session)
|
|
.with_variables(**step_variables)
|
|
.run()
|
|
)
|
|
step_data.data = case_result.step_datas # list of step data
|
|
step_data.export = case_result.get_export_variables()
|
|
step_data.success = case_result.success
|
|
self.success &= case_result.success
|
|
|
|
return step_data
|
|
|
|
def __run_step(self, step: TStep):
|
|
"""run teststep, teststep maybe a request or referenced testcase"""
|
|
logger.info(f"run step: {step.name}")
|
|
|
|
if step.request:
|
|
step_data = self.__run_step_request(step)
|
|
elif step.testcase:
|
|
step_data = self.__run_step_testcase(step)
|
|
else:
|
|
raise ParamsError(
|
|
f"teststep is neither a request nor a referenced testcase: {step.dict()}"
|
|
)
|
|
|
|
self.step_datas.append(step_data)
|
|
return step_data.export
|
|
|
|
def run(self):
|
|
"""main entrance"""
|
|
self.step_datas.clear()
|
|
self.session_variables.clear()
|
|
for step in self.teststeps:
|
|
# update with config variables
|
|
step.variables.update(self.config.variables)
|
|
# update with session variables extracted from former step
|
|
step.variables.update(self.session_variables)
|
|
# parse variables
|
|
step.variables = parse_variables_mapping(
|
|
step.variables, self.config.functions
|
|
)
|
|
# run step
|
|
extract_mapping = self.__run_step(step)
|
|
# save extracted variables to session variables
|
|
self.session_variables.update(extract_mapping)
|
|
|
|
return self
|
|
|
|
def get_export_variables(self):
|
|
export_vars_mapping = {}
|
|
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}"
|
|
)
|
|
|
|
export_vars_mapping[var_name] = self.session_variables[var_name]
|
|
|
|
return export_vars_mapping
|