pytest文档35-Hooks函数之统计测试结果(pytest_terminal_summary)

news/2024/11/24 4:58:44/

前言

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

pytest_terminal_summary

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

from _pytest import terminalpytest_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
from _pytest import terminal
# 上海-悠悠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
from _pytest import terminal
# 上海-悠悠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
from _pytest import terminal
# 作者-上海悠悠 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)


http://www.ppmy.cn/news/37391.html

相关文章

生成式 AI 背后的共同框架:Stable Diffusion、DALL-E、Imagen

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 框架 这些生成式 AI 的整体功能为&#xff1a;输入「文字」&#xff0c;返回「图像」&#xff0c;即 Text-to-image Gener…

[Java Web]Filter | 一文搞懂Web三大组件之一的Filter

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;Java Web 目录Filter1、概述2、执行流程2.1、简单介绍2.2、过滤器链3、代码实现3.1、实现步骤3.2、&#x1f53a…

STM32单片机WIFI-APP智能温控空调冷热可调红外遥控

STM32单片机WIFI-APP智能温控空调冷热可调红外遥控 实践制作DIY- GC0127-WIFI-APP智能温控空调 一、功能说明&#xff1a; 基于STM32单片机设计-WIFI-APP智能温控空调 二、功能介绍&#xff1a; STM32F103C系列最小系统板LCD1602显示器红外遥控电路DS18B20温度传感器1个继电…

Python轻量级Web框架Flask(3)——Flask路由参数/Flask请求与响应/重定项/异常处理

1、Flask路由参数和methods参数&#xff1a; 路由其实就是一个路径&#xff0c;就是a.route(‘/template_test/’)中的’/template_test/&#xff0c;每一个路由对应的是唯一的一个功能&#xff0c;如果要实现很多个功能&#xff0c;就需要很多个路由 methods参数就是用来设置…

【创作赢红包】<Windows>【技巧No.003】《Edge浏览器使用方法大全》(持续更新......)

《Edge浏览器使用技巧大全》1 插件1.1 插件获取1.2 广告拦截-AdGuard 广告拦截器1.3 自由复制网页文字-Simple Allow Copy1.4 图片另存为JPG/PNG/WebP1.5 标签页-itap新标签页1.6 网盘搜索-网盘资源全网搜索2 Edge自身功能2.1 朗读模式2.2 长截图2.3 下载加速1 插件 1.1 插件获…

java多线程相关知识讲解

一.多线程的基本概念。 进程&#xff1a;每个进程都有独立的代码和数据空间&#xff08;进程上下文&#xff09;&#xff0c;进程间的切换会有较大的开销&#xff0c;一个进程包含1--n个线程。&#xff08;进程是资源分配的最小单位&#xff09; 线程&#xff1a;同一类线程共…

PCB模块化设计04——USB-Type-C PCB布局布线设计规范

目录PCB模块化设计04——USB-Type-C PCB布局布线设计规范USB Type-C功能介绍信号图示Type-C接口引脚定义USB 2.0差分对电源和接地引脚RX和TX引脚CC1和CC2针脚VCONN引脚SBU1和SBU2针脚USB供电PCB设计布线要求PCB模块化设计04——USB-Type-C PCB布局布线设计规范 USB Type-C US…

39岁程序员,失业中,看不到希望很迷茫,不知道路在何方?

39岁程序员&#xff0c;失业中&#xff0c;看不到希望很迷茫&#xff0c;不知道路在何方&#xff1f;第一次看到这个问题时&#xff0c;挺无奈的。39岁这个年纪&#xff0c;照理也应该有10多年工作经验了&#xff0c;工作经验丰富自然不用怀疑&#xff0c;作为老程序员&#xf…