本文将会介绍pytest框架中一些常用的插件以及如何进行插件开发。
在文章pytest测试框架使用笔记(一) 中,笔者介绍了pytest测试框架的常见用法。本文将在此基础上,介绍基于pytest的常见插件的使用方法,以及如何进行插件开发。
pytest插件
是用于扩展pytest功能的模块,允许开发者根据特定需求自定义测试行为和增强测试报告等功能。
以下是一些常用的pytest插件及其功能:
pytest-html
生成HTML格式的测试报告
allure-pytest
集成Allure框架生成详细测试报告
pytest-xdist
支持并行执行测试用例
pytest-rerunfailures
允许对失败的测试用例进行重试
pytest-cov
生成代码覆盖率报告
通过这些插件,用户可以根据项目需求灵活调整和扩展pytest框架,从而提高测试效率和质量。
本文将会结合笔者日常的开发经验,介绍几个常用的pytest插件,以及如何进行定制化插件开发。
异步测试
在pytest中进行异步测试,通常会用到pytest-asyncio
插件,可直接使用pip方式安装。这个插件为pytest提供了对asyncio协程的支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import pytestimport asyncioasync def async_function (x ): await asyncio.sleep(1 ) return x * 2 @pytest.mark.asyncio async def test_async_function (): result = await async_function(3 ) assert result == 6
运行测试命令如下:
测试覆盖率
在单元测试中,通常需要对测试的文件生成代码覆盖率,而pytest-cov
插件能很好地生成代码覆盖率报告。
比如我们有如下的功能函数(文件名为add_func.py
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def my_add_func (a, b ): if isinstance (a, int ) and isinstance (b, int ): return a + b elif isinstance (a, float ) and isinstance (b, float ): return a + b elif isinstance (a, str ) and isinstance (b, str ): return a + b elif isinstance (a, int ) and isinstance (b, str ): return str (a) + b elif isinstance (a, list ) and isinstance (b, list ): return a + b else : return None
针对上述的函数,我们使用pytest进行单元测试,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import mathfrom add_func import my_add_funcdef test_my_add_func_case_1 (): assert my_add_func(1 , 2 ) == 3 def test_my_add_func_case_2 (): assert math.fabs(my_add_func(1.1 , 2.2 ) - 3.3 ) < 1e-9 def test_my_add_func_case_3 (): assert my_add_func("a" , "b" ) == "ab"
使用pytest-cov
命令输出代码覆盖率,命令如下:
1 pytest -v test_math_op.py --cov
输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 collected 3 items test_math_op.py::test_my_add_func_case_1 PASSED [ 33%] test_math_op.py::test_my_add_func_case_2 PASSED [ 66%] test_math_op.py::test_my_add_func_case_ 3 PASSED [100%]--------- coverage: platform darwin, python 3.10.12-final-0 ----------Name Stmts Miss Cover ------------------------------------- add_func.py 12 5 58% test_math_ op.py 8 0 100%------------------------------------- TOTAL 20 5 75%
如果需要生成代码覆盖率的HTML报告,则命令行为pytest -v test_math_op.py --cov --cov-report=html
。运行后会在当前目录下生成htmlconv文件夹下,在浏览器中打开其中的index.html文件,结果如下:
代码测试覆盖率
点击add_func.py
文件,可以查看该文件中哪些代码已经被测试通过,哪些代码没有加入单元测试。
生成HTML报告
pytest-html
插件可以生成HTML格式的测试报告。
保持上述的测试代码不变,运行HTML测试报告生成命令pytest -v test_math_op.py --html=report.html
,输出结果为:
1 2 3 4 5 6 7 collected 3 items test_math_op.py ::test_my_add_func_case_1 PASSED [ 33%] test_math_op.py ::test_my_add_func_case_2 PASSED [ 66%] test_math_op.py ::test_my_add_func_case_3 PASSED [100%] - Generated html report: file:
生成的HTML报告如下图:
生成HTML测试报告
上面方法生成的报告,CSS是独立的,分享报告的时候样式会丢失,为了更好的分享发邮件展示报告,可以把CSS样式合并到html里。
1 pytest -v test_math_op.py --html =report.html --self-contained-html
使用allure
工具,可以生成更美观的HTML报告,前提是你的电脑系统中已安装allure
工具并安装了pytest-allure
插件。
先生成allure报告:
1 pytest -v test_math_op.py --alluredir ./result
再生成HTML报告:
1 allure generate ./result/ -o ./report/ --clean
打开report文件夹中的index.html,显示结果如下:
重试机制
pytest-rerunfailures
允许对失败的测试用例进行重试。
我们有如下测试代码:
1 2 3 4 5 6 7 import randomdef test_example (): assert random.choice([True , False , False ])
对上述测试用例加入重试机制,命令行为pytest -v test_math_op.py --reruns 3 --reruns-delay 2
,输出结果如下:
1 2 3 4 5 collected 1 item test_math_op.py ::test_example RERUN [100%] test_math_op.py ::test_example RERUN [100%] test_math_op.py ::test_example PASSED [100%]
由于在上面的测试文件中,测试用例是随机选择的,所以我们的运行结果并不一定会重试。如果测试通过的话,则不会进行重试。
插件开发
如果我们需要进行定制化插件的开发,有两种可以选择的方案,一种是修改pytest模块所在的路径中
hookspec.py 文件中的hook函数,一种是修改conftest.py。
比如下面的测试用例:
1 2 3 4 5 6 7 8 import pytest@pytest.mark.parametrize("name" , ["张三" , "李四" , "王五" ] ) def test_name (name ): assert len (name) > 1
运行命令pytest -v test_math_op.py
,输出结果如下:
1 2 3 4 5 collected 3 items test_math_op.py ::test_name[\u5f20\u4e09] PASSED [ 33%] test_math_op.py ::test_name[\u674e\u56db] PASSED [ 66%] test_math_op.py ::test_name[\u738b\u4e94] PASSED [100%]
我们发现用例名字编码格式为Unicode,无法显示中文。如果需要显示中文,则在conftest.py中加入以下代码:
1 2 3 4 5 6 7 8 from typing import List def pytest_collection_modifyitems (session, config, items: List ): for item in items: item.name = item.name.encode('utf-8' ).decode('unicode-escape' ) item._nodeid = item.nodeid.encode('utf-8' ).decode('unicode-escape' ) items.reverse()
再次运行命令pytest -v test_math_op.py
,输出结果如下:
1 2 3 4 5 collected 3 items test_math_op.py ::test_name[王五] PASSED [ 33%] test_math_op.py ::test_name[李四] PASSED [ 66%] test_math_op.py ::test_name[张三] PASSED [100%]
总结
本文在pytest框架的常见用法上,介绍了一些常见的pytest插件使用方法,以及如何进行定制化的插件开发,希望能对读者有所启发~