【pytest】Hooks函数之统计测试结果(pytest_terminal_summary)

前言
用例执行完成后,我们希望能获取到执行的结果,这样方便我们快速统计用例的执行情况。
也可以把获取到的结果当成总结报告,发邮件的时候可以先统计测试结果,再加上html的报告。

pytest_terminal_summary
关于TerminalReporter类可以在_pytest.terminal中查看到

 pytest_terminal_summary(terminalreporter, exitstatus, config)
最后的结果汇总,可以拿到所有的执行结果
参数:
- terminalreporter (_pytest.terminal.TerminalReporter) – 内部使用的终端测试报告对象
- exitstatus (int) – 返回给操作系统的返回码
- config(_pytest.config.Config) - pytest config对象

TerminalReporter部分代码

class TerminalReporter(object):def __init__(self, config, file=None):import _pytest.configself.config = configself._numcollected = 0self._session = Noneself._showfspath = Noneself.stats = {}self.startdir = config.invocation_dirdef report_collect(self, final=False):if self.config.option.verbose < 0:returnif not final:# Only write "collecting" report every 0.5s.t = time.time()if (self._collect_report_last_write is not Noneand self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION):returnself._collect_report_last_write = terrors = len(self.stats.get("error", []))skipped = len(self.stats.get("skipped", []))deselected = len(self.stats.get("deselected", []))selected = self._numcollected - errors - skipped - deselectedif final:line = "collected "else:line = "collecting "line += (str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s"))if errors:line += " / %d errors" % errorsif deselected:line += " / %d deselected" % deselectedif skipped:line += " / %d skipped" % skippedif self._numcollected > selected > 0:line += " / %d selected" % selectedif self.isatty:self.rewrite(line, bold=True, erase=True)if final:self.write("\n")else:self.write_line(line)

案例参考

先在test_a.py写几个用例

# test_a.py
import pytest
# 上海-悠悠def test_1():print("测试用例1111")assert 1 == 1@pytest.mark.skip("跳过")
def test_2():print("测试用例22222")assert 1 == 1def test_3():print("测试用例3333")def test_4():print("测试用例44444444")assert 1 == 2

test_b.py用例参考

# test_b.py
import time
# 上海-悠悠def test_5():print("测试用例55555555")time.sleep(3)def test_6():print("测试用例66666666")time.sleep(3)assert 1 == 2

于是在conftest.py中写个 pytest_terminal_summary 函数收集测试结果

import time
# 上海-悠悠def pytest_terminal_summary(terminalreporter, exitstatus, config):'''收集测试结果'''print(terminalreporter.stats)print("total:", terminalreporter._numcollected)print('passed:', len(terminalreporter.stats.get('passed', [])))print('failed:', len(terminalreporter.stats.get('failed', [])))print('error:', len(terminalreporter.stats.get('error', [])))print('skipped:', len(terminalreporter.stats.get('skipped', [])))# terminalreporter._sessionstarttime 会话开始时间duration = time.time() - terminalreporter._sessionstarttimeprint('total times:', duration, 'seconds')

运行结果

D:\soft\pytest_xuexi_demo>pytest
============================= test session starts =============================
platform win32 -- Python 3.6.0, pytest-4.5.0, py-1.5.4, pluggy-0.13.1
rootdir: D:\soft\pytest_xuexi_demo
plugins: allure-pytest-2.8.6, PyTestReport-0.1.9.3, forked-0.2, html-1.19.0, metadata-1.7.0, repeat-0.7.0, rerunfailures-8.0, xdist-1.23.2
collected 6 itemstest_a.py .s.F                                                           [ 66%]
test_b.py .F                                                             [100%]================================== FAILURES ===================================
___________________________________ test_4 ____________________________________def test_4():print("测试用例44444444")
>       assert 1==2
E       assert 1 == 2test_a.py:21: AssertionError
---------------------------- Captured stdout call -----------------------------
测试用例44444444
___________________________________ test_6 ____________________________________def test_6():print("测试用例66666666")time.sleep(3)
>       assert 1 == 2
E       assert 1 == 2test_b.py:18: AssertionError
---------------------------- Captured stdout call -----------------------------
测试用例66666666
{'': [<TestReport 'test_a.py::test_1' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_1' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_2' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_3' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_3' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_4' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_4' when='teardown' outcome='passed'>, <TestReport 'test_b.py::test_5' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_5' when='teardown' outcome='passed'>, <TestReport 'test_b.py::test_6' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_6' when='teardown' outcome='passed'>], 'passed': [<TestReport 'test_a.py::test_1' when='call' outcome='passed'>, <TestReport 'test_a.py::test_3' when='call' outcome='passed'>, <TestReport 'test_b.py::test_5' when='call' outcome='passed'>], 'skipped': [<TestReport 'test_a.py::test_2' when='setup' outcome='skipped'>], 'failed': [<TestReport 'test_a.py::test_4' when='call' outcome='failed'>, <TestReport 'test_b.py::test_6' when='call' outcome='failed'>]}
total: 6
passed: 3
failed: 2
error: 0
skipped: 1
total times: 6.150860786437988 seconds
================ 2 failed, 3 passed, 1 skipped in 6.15 seconds ================

setup和teardown异常情况

如果setup出现异常,test_b.py代码修改下

# test_b.py
import time
import pytest
# 上海-悠悠@pytest.fixture(scope="function")
def setup_demo():raise TypeError("ERROR!")def test_5(setup_demo):print("测试用例55555555")time.sleep(3)def test_6():print("测试用例66666666")time.sleep(3)assert 1 == 2

重新运行用例,结果如下

total: 6
passed: 2
failed: 2
error: 1
skipped: 1
成功率:33.33%
total times: 3.1817877292633057 seconds
=========== 2 failed, 2 passed, 1 skipped, 1 error in 3.18 seconds ============

此时统计结果没什么问题,接下来看teardown异常情况

# test_b.py
import time
import pytest
# 上海-悠悠@pytest.fixture(scope="function")
def setup_demo():yield raise TypeError("ERROR!")def test_5(setup_demo):print("测试用例55555555")time.sleep(3)def test_6():print("测试用例66666666")time.sleep(3)assert 1 == 2

运行结果

{'': [<TestReport 'test_a.py::test_1' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_1' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_2' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_3' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_3' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_4' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_4' when='teardown' outcome='passed'>, <TestReport 'test_b.py::test_5' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_6' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_6' when='teardown' outcome='passed'>], 'passed': [<TestReport 'test_a.py::test_1' when='call' outcome='passed'>, <TestReport 'test_a.py::test_3' when='call' outcome='passed'>, <TestReport 'test_b.py::test_5' when='call' outcome='passed'>], 'skipped': [<TestReport 'test_a.py::test_2' when='setup' outcome='skipped'>], 'failed': [<TestReport 'test_a.py::test_4' when='call' outcome='failed'>, <TestReport 'test_b.py::test_6' when='call' outcome='failed'>], 'error': [<TestReport 'test_b.py::test_5' when='teardown' outcome='failed'>]}
total: 6
passed: 3
failed: 2
error: 1
skipped: 1
成功率:50.00%
total times: 6.18759298324585 seconds
=========== 2 failed, 3 passed, 1 skipped, 1 error in 6.19 seconds ============

这个时候总用例是6,但是2 failed, 3 passed, 1 skipped, 1 error加起来的个数是7,这个为什么?

从 terminalreporter.stats 可以看出 passed 里面 when='call' 时候统计了一次 test_5 用例

<TestReport 'test_b.py::test_5' when='call' outcome='passed'>

error 里面 when='teardown' 又统计了一次 test_5 用例

'error': [<TestReport 'test_b.py::test_5' when='teardown' outcome='failed'>]

when='teardown' 是测试用例的后置操作,一般用于数据的清理,报错了的话不影响测试用例的执行结果,所以可以忽略掉

修改后的最终代码如下

import time
# 上海-悠悠def pytest_terminal_summary(terminalreporter, exitstatus, config):'''收集测试结果'''# print(terminalreporter.stats)print("total:", terminalreporter._numcollected)print('passed:', len([i for i in terminalreporter.stats.get('passed', []) if i.when != 'teardown']))print('failed:', len([i for i in terminalreporter.stats.get('failed', []) if i.when != 'teardown']))print('error:', len([i for i in terminalreporter.stats.get('error', []) if i.when != 'teardown']))print('skipped:', len([i for i in terminalreporter.stats.get('skipped', []) if i.when != 'teardown']))print('成功率:%.2f' % (len(terminalreporter.stats.get('passed', []))/terminalreporter._numcollected*100)+'%')# terminalreporter._sessionstarttime 会话开始时间duration = time.time() - terminalreporter._sessionstarttimeprint('total times:', duration, 'seconds')

运行后的结果

total: 6
passed: 3
failed: 2
error: 0
skipped: 1
成功率:50.00%
total times: 6.20133113861084 seconds
=========== 2 failed, 3 passed, 1 skipped, 1 error in 6.20 seconds ============

拿到测试结果

很多小伙伴问到如何拿到测试结果,这里我把测试结果保存为txt文件,你们也可以保存json文件

import time
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/def pytest_terminal_summary(terminalreporter, exitstatus, config):'''收集测试结果'''# print(terminalreporter.stats)total = terminalreporter._numcollectedpassed= len([i for i in terminalreporter.stats.get('passed', []) if i.when != 'teardown'])failed=len([i for i in terminalreporter.stats.get('failed', []) if i.when != 'teardown'])error=len([i for i in terminalreporter.stats.get('error', []) if i.when != 'teardown'])skipped=len([i for i in terminalreporter.stats.get('skipped', []) if i.when != 'teardown'])successful = len(terminalreporter.stats.get('passed', []))/terminalreporter._numcollected*100# terminalreporter._sessionstarttime 会话开始时间duration = time.time() - terminalreporter._sessionstarttimeprint('total times: %.2f' % duration, 'seconds')with open("result.txt", "w") as fp:fp.write("TOTAL=%s" % total+"\n")fp.write("PASSED=%s" % passed+"\n")fp.write("FAILED=%s" % failed+"\n")fp.write("ERROR=%s" % error+"\n")fp.write("SKIPPED=%s" % skipped+"\n")fp.write("SUCCESSFUL=%.2f%%" % successful+"\n")fp.write("TOTAL_TIMES=%.2fs" % duration)

邮件发送

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

文档获取方式: 

加入我的软件测试交流群:822269834免费获取~(同行大佬一起学术交流,每晚都有大佬直播分享技术知识点)

这份文档,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

以上均可以分享,只需要你搜索vx公众号:程序员雨果,即可免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/217501.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Spring源码】Spring Event事件

目录 1、前言 2、什么是Spring Event&#xff1f; 3、基本使用 3.1、定义事件 3.2、发布事件 3.3、监听事件 3.3.1、继承ApplicationListener 3.3.2、使用EventListener注解 4、Spring Event是同步还是异步&#xff1f; 4.1、源码实现 4.2、如何实现异步 4.2.1、使用…

【教学类-06-09】20231125 (55格版)X-Y之间“加法减法+-题” (以10-20之间为例)(加法的正序+逆序,减法的正序,题目多)

图片展示 需求&#xff1a; 20以内加法减法&#xff0c;不需要再练习其中10以内部分&#xff0c;改为10-20以内的加法减法&#xff0c;X-Y大于10&#xff0c;小于20的所有加法减法题。 代码展示&#xff1a; X-Y 之间的所有加减混合法题&#xff08;如10-20之间的所有加法减法…

间接法加窗分析信号的功率谱

本篇文章是博主在通信等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对通信等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在 通信领域笔记&#xff…

力扣学习笔记——239. 滑动窗口最大值

力扣学习笔记——239. 滑动窗口最大值 题目描述 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输…

SSF-CNN:空谱融合的简易卷积超分网络

SSF-CNN: SPATIAL AND SPECTRAL FUSION WITH CNN FOR HYPERSPECTRAL IMAGE SUPER-RESOLUTION 文章目录 SSF-CNN: SPATIAL AND SPECTRAL FUSION WITH CNN FOR HYPERSPECTRAL IMAGE SUPER-RESOLUTION简介解决问题网络框架代码实现训练部分运行结果 简介 ​ 本文提出了一种利用空…

【web】Fastapi自动生成接口文档(Swagger、ReDoc )

简介 FastAPI是流行的Python web框架&#xff0c;适用于开发高吞吐量API和微服务&#xff08;直接支持异步编程&#xff09; FastAPI的优势之一&#xff1a;通过提供高级抽象和自动数据模型转换&#xff0c;简化请求数据的处理&#xff08;用户不需要手动处理原始请求数据&am…

2024年天津天狮学院专升本护理学专业《内外科护理学》考试大纲

天津天狮学院2024年护理学专业高职升本入学考试《内外科护理学》考试大纲 一、考试性质 《内外科护理学》专业课程考试是天津天狮学院护理专业高职升本入学考试的必考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的选拔性考试。《内外科护理学…

UWB实时定位系统源码,历史活动轨迹显示,视频联动,电子围栏

UWB实时定位系统源码&#xff0c;工厂企业人员安全定位&#xff0c;UWB源码 行业背景 工业企业多存在很多有毒有害、高危高压等生产环境&#xff0c;带电设备众多&#xff0c;容易发生安全事故&#xff1b;人员只能凭记忆遵守各项生产安全规范&#xff0c;如某些危险区域范围、…

C++入门第九篇---Stack和Queue模拟实现,优先级队列

前言&#xff1a; 我们已经掌握了string vector list三种最基本的数据容器模板&#xff0c;而对于数据结构的内容来说&#xff0c;其余的数据结构容器基本都是这三种容器的延申和扩展&#xff0c;在他们的基础上扩展出更多功能和用法&#xff0c;今天我们便来模拟实现一下C库中…

Word打印模板,打印效果更出众丨三叠云

Word打印模板 路径 表单设置 >> 打印设置 功能简介 新增「Word打印模板」(beta版)。 Word 打印模板是指&#xff0c;在 Word 文档的基础上插入表单中的字段代码&#xff0c;打印时即可根据 Word 文档的格式&#xff0c;对表单数据进行个性化打印。 Word 打印模板能…

rancher2.6 docker版本部署

1. 拉取镜像 docker pull rancher/rancher:v2.6.5 注&#xff1a; 上面命令中rancher的版本v2.6.5&#xff0c;仅仅是我因为我们环境中使用的k8s都是 1.20.1 到1.23.6 之间的版本。rancher支持的k8s版本&#xff0c;在github上查看&#xff1a;Release Release v2.6.5 ranche…

探索 Vue 中的 bus.$emit:实现组件通信的强大工具

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…