> 文章列表 > HttpRunner3.x 源码解析(5)-runner.py

HttpRunner3.x 源码解析(5)-runner.py

HttpRunner3.x 源码解析(5)-runner.py

首先看下生成的pytest文件

from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCaseclass TestCaseLogin(HttpRunner):config = (Config("登录成功").variables(**{"password": "tester", "expect_foo2": "config_bar2"}).base_url("https://api.pity.fun").verify(False).export(*["token"]))teststeps = [Step(RunRequest("登录成功").with_variables(**{"foo1": "bar1"}).setup_hook("${get_request($request)}").post("/auth/login").with_headers(**{"Content-Type": "application/json"}).with_json({"username": "${ENV(username)}", "password": "${ENV(password)}"}).teardown_hook("${get_reponse($response)}").extract().with_jmespath("body.msg", "token").validate().assert_equal("status_code", 200).assert_equal("body.code", 0).assert_equal("body.msg", "哈哈哈")),]if __name__ == "__main__":TestCaseLogin().test_start()

首先类继承了HttpRunner类

主要包括config和teststeps两部分

函数的入库是test_start()方法

test_start

  def test_start(self, param: Dict = None) -> "HttpRunner":"""main entrance, discovered by pytest"""self.__init_tests__()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.RootDir, "logs", f"{self.__case_id}.run.log")log_handler = logger.add(self.__log_path, level="DEBUG")# parse config nameconfig_variables = self.__config.variablesif param:config_variables.update(param)config_variables.update(self.__session_variables)self.__config.name = parse_data(self.__config.name, config_variables, self.__project_meta.functions)if USE_ALLURE:# update allure report metaallure.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}")try:return self.run_testcase(TestCase(config=self.__config, teststeps=self.__teststeps))finally:logger.remove(log_handler)logger.info(f"generate testcase log: {self.__log_path}")

首先调用了__init_tests__()方法

 teststeps是pytest文件中的Step列表,示例只有1个步骤

 

config.perform方法返回了一个Tconfig对象,这个对象定义了testcase的config中的关键字和内容

 

teststeps是py文件中的Step列表,

调用的step.perform()方法,返回__step_context,而__step_context来自step_context调用perfoem方法。

在Step类的初始化函数可以看到,step_context可以是如下几种


当step_contxt时RunTestCase时,调用它的perfom方法如下,可以看到返回的是一个TStep对象。

 

 接着看test_start()

self.__project_meta = self.__project_meta or load_project_meta(self.__config.path
)

 返回项目的资源文件

self.__case_id为caseid或者是一个uuid
self.__log_path为日志目录
config_variables = self.__config.variables为config部分的变量
self.__config.name = parse_data(self.__config.name, config_variables, self.__project_meta.functions
)获取config中的name
if USE_ALLURE:# update allure report metaallure.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}"
)

如果用了allure报告,则定义title和描述

 

return self.run_testcase(TestCase(config=self.__config, teststeps=self.__teststeps)
)

将config和teststeps列表传给TestCase并调用HttpRunner类的run_testcase方法执行用例

run_testcase

这个方法最后返回的也是HttpRunner实例

主要的是下面这一句,执行了用例的步骤。

extract_mapping = self.__run_step(step)

 __run_step方法

如果步骤中存在request,则调用__run_step_request来执行步骤,并返回返回testdata

如果存在testcase,则调用__run_step_testcase(step)来执行,最后返回test_data

    def __run_step(self, step: TStep):"""run teststep, teststep maybe a request or referenced testcase"""logger.info(f"run step begin: {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)logger.info(f"run step end: {step.name} <<<<<<\\n")return step_data.export_vars

__run_step_request

这个方法解析request并调用request方法来执行

resp = self.__session.request(method, url, **parsed_request_dict)

 

parsed_request_dict解析请求字典

parsed_request_dict

{'method': 'POST', 'url': '/auth/login', 'params': {}, 'headers': {'Content-Type': 'application/json'}, 'req_json': {'username': 'tester', 'password': 'tester'}, 'data': None, 'cookies': {}, 'timeout': 120, 'allow_redirects': True, 'verify': False}

if step.setup_hooks:self.__call_hooks(step.setup_hooks, step.variables, "setup request")

如果存在setup_hooks就调用hook

 

 

# teardown hooks
if step.teardown_hooks:self.__call_hooks(step.teardown_hooks, step.variables, "teardown request")

如果存在teardown就调用hook

 最后返回step_data