pytest测试框架使用笔记(二)插件的使用与开发

本文将会介绍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
# @file: test_file.py
import pytest
import asyncio

# 被测异步函数
async 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

运行测试命令如下:

1
pytest -v test_file.py

测试覆盖率

在单元测试中,通常需要对测试的文件生成代码覆盖率,而pytest-cov插件能很好地生成代码覆盖率报告。

比如我们有如下的功能函数(文件名为add_func.py):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding: utf-8 -*-
# @file: add_func.py


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
# -*- coding: utf-8 -*-
# @file: test_math_op.py
import math
from add_func import my_add_func


def 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:///Users/admin/PycharmProjects/env_test/pytest_learning/report.html -

生成的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
# -*- coding: utf-8 -*-
# @file: test_math_op.py
import random


def 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
# -*- coding: utf-8 -*-
# @file: test_math_op.py
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插件使用方法,以及如何进行定制化的插件开发,希望能对读者有所启发~


pytest测试框架使用笔记(二)插件的使用与开发
https://percent4.github.io/pytest测试框架使用笔记(二)插件的使用与开发/
作者
Jclian91
发布于
2025年1月8日
许可协议