Files
httprunner/httprunner/runner.py
2020-05-15 00:04:18 +08:00

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