HttpRunner3的用例是怎么运行起来的
在PyCharm中打开examples/httpbin/basic_test.py
:
首先映入眼帘的是左上角那个绿色小箭头,点了一下,可以直接运行,意味着HttpRunner是能够直接被pytest驱动运行的,这可就有点意思了,难道HttpRunner的底层是pytest?带着这个疑问我全局搜索了一下pytest:
在cli.py文件中,如果参数是run,那么会执行pytest.main(["h"])
,难道真是我猜测的这样?在basic_test.py
最后有两行代码:
if __name__ == "__main__":
TestCaseBasic().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 name
config_variables = self.__config.variables
if 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 meta
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}"
)
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}")
第一行注释就是证明了我的猜想是对的:main entrance, discovered by pytest,主程序入口,会被pytest发现。本文不去探究每行代码是什么意思,重点关注跟pytest相关的运行流程。跟着这段代码:
return self.run_testcase(
TestCase(config=self.__config, teststeps=self.__teststeps)
)
继续往下走,调用了self.run_testcase,它的源码如下:
def run_testcase(self, testcase: TestCase) -> "HttpRunner":
"""run specified testcase
Examples:
>>> testcase_obj = TestCase(config=TConfig(...), teststeps=[TStep(...)])
>>> HttpRunner().with_project_meta(project_meta).run_testcase(testcase_obj)
"""
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.__start_at = time.time()
self.__step_datas: List[StepData] = []
self.__session = self.__session or HttpSession()
# save extracted variables of teststeps
extracted_variables: VariablesMapping = {}
# run teststeps
for step in self.__teststeps:
# override variables
# step variables > extracted variables from previous steps
step.variables = merge_variables(step.variables, extracted_variables)
# step variables > testcase config variables
step.variables = merge_variables(step.variables, self.__config.variables)
# parse variables
step.variables = parse_variables_mapping(
step.variables, self.__project_meta.functions
)
# run step
if USE_ALLURE:
with allure.step(f"step: {step.name}"):
extract_mapping = self.__run_step(step)
else:
extract_mapping = self.__run_step(step)
# save extracted variables to session variables
extracted_variables.update(extract_mapping)
self.__session_variables.update(extracted_variables)
self.__duration = time.time() - self.__start_at
return self
跟着这段代码:
# run step
if USE_ALLURE:
with allure.step(f"step: {step.name}"):
extract_mapping = self.__run_step(step)
else:
extract_mapping = self.__run_step(step)
继续往下走,self.__run_step的源码如下:
def __run_step(self, step: TStep) -> Dict:
"""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
有两个分支:
if step.request:
step_data = self.__run_step_request(step)
elif step.testcase:
step_data = self.__run_step_testcase(step)
self.__run_step_request(step)
直接调用的request:
resp = self.__session.request(method, url, **parsed_request_dict)
self.__run_step_testcase(step)
直接调用的HttpRunner():
case_result = (
testcase_cls()
.with_session(self.__session)
.with_case_id(self.__case_id)
.with_variables(step_variables)
.with_export(step_export)
.run()
)
真相只有一个,一定在HttpRunner里面。HttpRunner是run.py模块里面的一个类:
刚才看到所有代码,其实都是在runner.py模块的HttpRunner类里面。看看run函数的代码:
def run(self) -> "HttpRunner":
""" run current testcase
Examples:
>>> TestCaseRequestWithFunctions().run()
"""
self.__init_tests__()
testcase_obj = TestCase(config=self.__config, teststeps=self.__teststeps)
return self.run_testcase(testcase_obj)
又调用了self.run_testcase,循环回去了。
貌似陷入了死循环,实际上答案已经有了,这不就是递归么?再回头来看刚才这两个分支:
如果是request,那么就调用self.__session.request(method, url, **parsed_request_dict)
,这是递归的终止条件:
如果是testcase,那么表示这是子用例,那么就递归下去,这是递归的子表达式:
原来,通过TestCaseBasic().test_start()
来执行测试,并没有调pytest,而是直接通过requests发送HTTP请求的,控制台和文件日志也是使用loguru库来自定义输出的。不得不对源码佩服得五体投地。
回到开头那个问题,为什么还有pytest的相关代码呢,实际上如果是通过命令行的run来执行用例,那么就是用直接用的pytest了:
一句话总结:如果是用命令行的run命令,那么就是通过pytest来调用的;如果是用代码里的test_start()方法,那么就是调requests作者自创的。
最后一个问题是,为什么在PyCharm中点那个绿色的小箭头,也能运行代码呢,答案很简单,这个类TestCaseBasic是Test
开头的,这个方法test_start是test_
开头的,这不就是pytest的规则么。
HttpRunner3的用例是怎么运行起来的的更多相关文章
- unittest 运行slenium(四)---通过指定用例的形式运行用例
一: 说明 跟数据驱动唯一的区别为用例数据获取时,及运行方式不同. 其它都基本相同,可参考https://www.cnblogs.com/xiaodingdong/p/11753220.html 二: ...
- pytest之收集用例规则与运行指定用例
前言 上篇文章相信大家已经了解了pytest在cmd下结合各种命令行参数如何运行测试用例,并输出我们想要看到的信息.那么今天会讲解一下pytest是如何收集我们写好的用例?我们又有哪些方式来运行单个用 ...
- RobotFramework_2.新建项目、新建用例、运行用例和查看测试报告
RobotFramework的使用 新建一个项目 Robotframework-ride的界面 分了四个区域:菜单栏.工具栏.案例及资源区.工作区,如下图: 创建项目 首先,点击File-New Pr ...
- HttpRunner学习11--指定用例运行次数
前言 在HttpRunner中,一般情况下,我们写的用例脚本都是每次运行一次,如果我们想要指定用例运行的次数,可以通过 times 关键字来实现. 测试场景 在这里,我们以访问 TesterHome ...
- pycharm中Terminal中运行用例
1.设置终端路径 2.单个用例文件运行 3.多个用例文件,例如加载用例的文件运行 1.可能会出现如下错误(参考:https://blog.csdn.net/qq_36829091/article/d ...
- Android 6.0的运行时权限
原文 http://droidyue.com/blog/2016/01/17/understanding-marshmallow-runtime-permission/ 主题 安卓开发 Andr ...
- Storm的本地运行模式示例
以word count为例,本地化运行模式(不需要安装zookeeper.storm集群),maven工程, pom.xml文件如下: <project xmlns="http://m ...
- WIN7 X64 如何运行debug
WIN7 X64 如何运行debug 下载debug.exe 安装dosbox 安装完成后将debug.exe放入某个盘的根目录下(以c盘为例) 然后运行dosbox,输入 mount c c:\ c ...
- 聊一聊 Android 6.0 的运行时权限
权限一刀切 棉花糖运行时权限 权限的分组 正常权限 正常权限列表 特殊权限危险权限 请求SYSTEM_ALERT_WINDOW 请求WRITE_SETTINGS 必须要支持运行时权限么 不支持运行时权 ...
随机推荐
- 我写了个IDEA开源插件,vo2dto 一键生成对象转换
让人头疼的对象转换 头炸,po2vo.vo2do.do2dto,一堆对象属性,取出来塞进来.要不是为了 DDD 架构下的各个分层防腐,真想一竿子怼下去. 那上 BeanUtils.copyProper ...
- 最强最全面的大数据SQL经典面试题(由31位大佬共同协作完成)
本套SQL题的答案是由许多小伙伴共同贡献的,1+1的力量是远远大于2的,有不少题目都采用了非常巧妙的解法,也有不少题目有多种解法.本套大数据SQL题不仅题目丰富多样,答案更是精彩绝伦! 注:以下参考答 ...
- P7990-[USACO21DEC]Closest Cow Wins S【堆,贪心】
正题 题目链接:https://www.luogu.com.cn/problem/P7990 题目大意 数轴上有\(k\)个点是草地,每个草地有不同收益,\(m\)个点是地方的点,现在你要放置\(n\ ...
- SpringBoot统一日志打印
统一日志打印 @Slf4j @Aspect @Component public class ControllerLog { private static final ThreadLocal<Lo ...
- 【LeetCode】812. Largest Triangle Area 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 三重循环 组合函数 日期 题目地址:https:// ...
- How Many Sets I(zoj3556)
How Many Sets I Time Limit: 2 Seconds Memory Limit: 65536 KB Give a set S, |S| = n, then how ma ...
- 另一个角度看元宇宙与RPA:人工世界、平行员工与RPA
另一个角度看元宇宙与RPA:人工世界.平行员工与RPA 从元宇宙到平行员工,人工世界推动的虚实分工利好RPA 机器人是铁打营盘人类是流水兵,未来元宇宙的虚实分工RPA机会巨大 文/王吉伟 元宇宙是平行 ...
- 『学了就忘』vim编辑器基础 — 96、末行模式中的相关命令
目录 1.在文档中显示行号 2.是否显示文档内容相关颜色 3.是否将查找的字符串高亮显示 4.是否显示右下角的状态栏 5.是否在左下角显示如"--INSERT--"之类的状态栏 6 ...
- 【C/C++笔记】友元类函数
最近学了友元,有三个用法: 1友元函数 2友元类 3友元类函数 我发现友元类函数的用法要比上两个用法要严格,不按格式写会各种出错,要把两个类都拆开来写,共分4步. 第一步: class A; //有 ...
- Handing Incomplete Heterogeneous Data using VAEs
目录 概 主要内容 ELBO 网络结构 不同的数据 HI-VAE 代码 Nazabal A., Olmos P., Ghahramani Z. and Valera I. Handing incomp ...