Pytest 常用的第三方插件详解
1. 前言
1.1 什么是 Pytest 插件
Pytest 插件是扩展 pytest 功能的第三方库。Pytest 本身提供了强大的测试框架基础,而插件则在此基础上添加了更多实用的功能,比如生成HTML报告、并行执行测试、代码覆盖率统计、失败重试等。
1.2 为什么要使用插件
- 提高效率:并行执行、失败重试等功能可以节省时间
- 增强功能:添加pytest本身没有的功能,如HTML报告、代码覆盖率
- 改善体验:美化输出、立即显示失败信息等让测试更友好
- 专业工具:使用专业插件让测试更规范、更专业
1.3 如何安装插件
基本安装命令:
# 使用 pip 安装单个插件
pip install pytest-html
# 使用 pip 安装多个插件
pip install pytest-html pytest-xdist pytest-cov
# 从 requirements.txt 安装
pip install -r requirements.txt
查看已安装的插件:
# 查看所有已安装的 pytest 插件
pytest --trace-config
# 或者
pip list | grep pytest
1.4 插件配置文件
pytest.ini 配置示例:
[pytest]
# 添加命令行选项
addopts = -v --tb=short
# 指定测试目录
testpaths = tests
# 指定测试文件匹配模式
python_files = test_*.py
# 指定测试类匹配模式
python_classes = Test*
# 指定测试函数匹配模式
python_functions = test_*
pyproject.toml 配置示例:
[tool.pytest.ini_options]
addopts = "-v --tb=short"
testpaths = ["tests"]
python_files = "test_*.py"
python_classes = "Test*"
python_functions = "test_*"
2. pytest-html:生成 HTML 测试报告
2.1 插件简介
pytest-html 是 pytest 最常用的插件之一,它可以生成美观的 HTML 格式测试报告,让测试结果更直观、更易读。
2.2 安装方法
pip install pytest-html
2.3 基本用法
2.3.1 生成基本 HTML 报告
命令:
pytest --html=report.html
示例:
# 运行测试并生成报告
pytest test_example.py --html=report.html
# 生成报告到指定目录
pytest --html=reports/report.html
2.3.2 查看报告
生成报告后,直接用浏览器打开 report.html 文件即可查看。
2.4 详细示例
示例 1:基本使用
测试文件:test_calculator.py
def test_addition():
"""测试加法"""
assert 1 + 1 == 2
def test_subtraction():
"""测试减法"""
assert 5 - 3 == 2
def test_multiplication():
"""测试乘法"""
assert 2 * 3 == 6
def test_division():
"""测试除法"""
assert 10 / 2 == 5
def test_division_by_zero():
"""测试除零错误"""
try:
result = 1 / 0
except ZeroDivisionError:
assert True
运行命令:
pytest test_calculator.py --html=report.html --self-contained-html
参数说明:
--html=report.html:生成HTML报告--self-contained-html:生成自包含的HTML(包含所有CSS和JS,方便分享)
示例 2:自定义报告标题
命令:
pytest --html=report.html --title="我的测试报告"
示例 3:在报告中包含额外信息
测试文件:test_with_extra_info.py
import pytest
def test_with_extra_info():
"""测试带额外信息"""
# 使用 pytest 的 extra 功能添加额外信息
pytest.extra = [
("环境", "测试环境"),
("版本", "1.0.0"),
("执行人", "张三")
]
assert 1 + 1 == 2
2.5 高级配置
2.5.1 在 pytest.ini 中配置
[pytest]
addopts = --html=report.html --self-contained-html
2.5.2 自定义报告模板
# conftest.py
import pytest
from pytest_html import extras
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if report.when == "call":
# 添加截图(如果有)
if hasattr(item, "screenshot"):
extra = getattr(report, "extra", [])
extra.append(extras.image(item.screenshot))
report.extra = extra
2.6 报告内容说明
HTML 报告通常包含:
- 测试摘要:总测试数、通过数、失败数、跳过数
- 测试详情:每个测试的执行时间、状态、错误信息
- 环境信息:Python版本、pytest版本、操作系统等
- 统计图表:可视化测试结果
3. pytest-xdist:并行执行测试
3.1 插件简介
pytest-xdist 允许你并行运行测试,大大缩短测试执行时间。特别适合有大量测试用例的项目。
3.2 安装方法
pip install pytest-xdist
3.3 基本用法
3.3.1 使用所有 CPU 核心
命令:
pytest -n auto
这会自动检测 CPU 核心数并使用所有核心并行执行测试。
3.3.2 指定并行进程数
命令:
# 使用 4 个进程
pytest -n 4
# 使用 2 个进程
pytest -n 2
3.4 详细示例
示例 1:基本并行执行
测试文件:test_parallel.py
import time
def test_1():
"""测试1 - 模拟耗时操作"""
time.sleep(1)
assert True
def test_2():
"""测试2 - 模拟耗时操作"""
time.sleep(1)
assert True
def test_3():
"""测试3 - 模拟耗时操作"""
time.sleep(1)
assert True
def test_4():
"""测试4 - 模拟耗时操作"""
time.sleep(1)
assert True
def test_5():
"""测试5 - 模拟耗时操作"""
time.sleep(1)
assert True
串行执行(不使用 xdist):
pytest test_parallel.py -v
# 执行时间:约 5 秒(5个测试 × 1秒)
并行执行(使用 xdist):
pytest test_parallel.py -n auto -v
# 执行时间:约 1-2 秒(取决于CPU核心数)
示例 2:查看并行执行详情
命令:
pytest -n 4 -v --dist=worksteal
参数说明:
-n 4:使用4个进程--dist=worksteal:使用工作窃取算法分配任务(默认)
示例 3:按文件并行
命令:
pytest -n auto --dist=loadfile
参数说明:
--dist=loadfile:按文件分配,同一文件的测试在同一进程中执行
3.5 并行模式说明
3.5.1 load(默认模式)
每个进程独立执行测试,适合大多数情况。
pytest -n 4 --dist=load
3.5.2 loadscope
按作用域(模块、类)分配测试。
pytest -n 4 --dist=loadscope
3.5.3 loadfile
按文件分配测试,同一文件的测试在同一进程。
pytest -n 4 --dist=loadfile
3.5.4 worksteal
使用工作窃取算法,动态分配任务。
pytest -n 4 --dist=worksteal
3.6 注意事项
- 测试独立性:并行测试要求测试用例之间相互独立,不能有依赖关系
- 共享资源:注意文件、数据库等共享资源的并发访问问题
- fixture 作用域:注意 fixture 的作用域,避免并发问题
示例:有依赖的测试(不适合并行)
# ❌ 错误示例:测试之间有依赖
class TestCounter:
count = 0 # 类变量,会被多个进程共享
def test_increment_1(self):
TestCounter.count += 1
assert TestCounter.count == 1
def test_increment_2(self):
TestCounter.count += 1
assert TestCounter.count == 2 # 可能失败,因为并行执行
示例:独立的测试(适合并行)
# ✅ 正确示例:测试之间独立
def test_addition():
assert 1 + 1 == 2
def test_subtraction():
assert 5 - 3 == 2
def test_multiplication():
assert 2 * 3 == 6
4. pytest-cov:代码覆盖率统计
4.1 插件简介
pytest-cov 用于统计测试的代码覆盖率,帮助你了解哪些代码被测试覆盖,哪些代码没有被测试。
4.2 安装方法
pip install pytest-cov
注意:pytest-cov 依赖于 coverage 库,通常会自动安装。
4.3 基本用法
4.3.1 生成覆盖率报告
命令:
pytest --cov=src
参数说明:
--cov=src:统计src目录的代码覆盖率
4.3.2 生成 HTML 覆盖率报告
命令:
pytest --cov=src --cov-report=html
这会在 htmlcov 目录生成 HTML 报告。
4.3.3 在终端显示覆盖率
命令:
pytest --cov=src --cov-report=term
4.4 详细示例
示例 1:基本覆盖率统计
项目结构:
project/
├── src/
│ ├── calculator.py
│ └── utils.py
└── tests/
└── test_calculator.py
源代码:src/calculator.py
class Calculator:
def add(self, a, b):
"""加法"""
return a + b
def subtract(self, a, b):
"""减法"""
return a - b
def multiply(self, a, b):
"""乘法"""
return a * b
def divide(self, a, b):
"""除法"""
if b == 0:
raise ValueError("除数不能为0")
return a / b
测试文件:tests/test_calculator.py
from src.calculator import Calculator
def test_add():
calc = Calculator()
assert calc.add(1, 2) == 3
def test_subtract():
calc = Calculator()
assert calc.subtract(5, 3) == 2
# 注意:没有测试 multiply 和 divide 方法
运行覆盖率统计:
pytest tests/test_calculator.py --cov=src --cov-report=term-missing
输出示例:
Name Stmts Miss Cover Missing
-----------------------------------------------------
src/calculator.py 10 4 60% 7-9, 12
src/utils.py 5 5 0% 1-5
-----------------------------------------------------
TOTAL 15 9 40%
说明:
Stmts:总语句数Miss:未覆盖的语句数Cover:覆盖率百分比Missing:未覆盖的行号
示例 2:生成 HTML 报告
命令:
pytest --cov=src --cov-report=html --cov-report=term
查看报告:
打开 htmlcov/index.html 文件,可以看到:
- 每个文件的覆盖率
- 每行代码的覆盖情况(绿色=已覆盖,红色=未覆盖)
- 覆盖率统计图表
示例 3:设置覆盖率阈值
命令:
pytest --cov=src --cov-fail-under=80
说明:
--cov-fail-under=80:如果覆盖率低于80%,测试失败
在 pytest.ini 中配置:
[pytest]
addopts = --cov=src --cov-report=html --cov-report=term --cov-fail-under=80
示例 4:排除特定文件或目录
命令:
pytest --cov=src --cov-report=term --ignore=src/tests
在 .coveragerc 文件中配置:
[run]
source = src
omit =
*/tests/*
*/test_*.py
*/__pycache__/*
*/venv/*
4.5 覆盖率报告格式
4.5.1 term(终端显示)
pytest --cov=src --cov-report=term
4.5.2 term-missing(显示未覆盖的行)
pytest --cov=src --cov-report=term-missing
4.5.3 html(HTML报告)
pytest --cov=src --cov-report=html
4.5.4 xml(XML报告,用于CI/CD)
pytest --cov=src --cov-report=xml
4.5.5 json(JSON报告)
pytest --cov=src --cov-report=json
4.6 高级配置
4.6.1 .coveragerc 配置文件
[run]
source = src
omit =
*/tests/*
*/__pycache__/*
*/venv/*
[report]
exclude_lines =
pragma: no cover
def __repr__
raise AssertionError
raise NotImplementedError
if __name__ == .__main__.:
if TYPE_CHECKING:
@abstractmethod
4.6.2 在代码中排除特定行
def example_function():
if some_condition: # pragma: no cover
return "rare case"
return "normal case"
5. pytest-timeout:测试超时控制
5.1 插件简介
pytest-timeout 可以为测试用例设置超时时间,防止测试无限期运行。如果测试在指定时间内没有完成,会被自动终止。
5.2 安装方法
pip install pytest-timeout
5.3 基本用法
5.3.1 全局超时设置
命令:
pytest --timeout=10
说明:所有测试用例的超时时间为 10 秒。
5.3.2 使用装饰器设置单个测试超时
测试文件:test_timeout.py
import pytest
import time
@pytest.mark.timeout(5) # 5秒超时
def test_quick():
"""快速测试"""
assert 1 + 1 == 2
@pytest.mark.timeout(2) # 2秒超时
def test_slow():
"""慢速测试(会超时)"""
time.sleep(3) # 睡眠3秒,超过2秒超时
assert True
运行:
pytest test_timeout.py -v
输出:
test_timeout.py::test_quick PASSED
test_timeout.py::test_slow TIMEOUT (超过2秒)
5.4 详细示例
示例 1:全局超时
测试文件:test_global_timeout.py
import time
def test_fast():
"""快速测试"""
time.sleep(0.5)
assert True
def test_medium():
"""中等速度测试"""
time.sleep(2)
assert True
def test_slow():
"""慢速测试(会超时)"""
time.sleep(5) # 超过3秒全局超时
assert True
运行命令:
pytest test_global_timeout.py --timeout=3 -v
示例 2:使用装饰器
测试文件:test_decorator_timeout.py
import pytest
import time
@pytest.mark.timeout(1)
def test_with_timeout_1():
"""1秒超时"""
time.sleep(0.5)
assert True
@pytest.mark.timeout(2)
def test_with_timeout_2():
"""2秒超时"""
time.sleep(1.5)
assert True
@pytest.mark.timeout(0.5)
def test_timeout_fail():
"""0.5秒超时,但需要1秒,会失败"""
time.sleep(1)
assert True
运行:
pytest test_decorator_timeout.py -v
示例 3:在 pytest.ini 中配置
[pytest]
timeout = 10
timeout_method = thread
配置说明:
timeout = 10:默认超时时间10秒timeout_method = thread:使用线程方式(默认)timeout_method = signal:使用信号方式(仅Unix系统)
5.5 超时方法说明
5.5.1 thread 方法(默认)
使用线程来监控超时,适用于所有平台。
pytest --timeout=10 --timeout-method=thread
5.5.2 signal 方法
使用信号来监控超时,仅适用于Unix系统(Linux、macOS),Windows不支持。
pytest --timeout=10 --timeout-method=signal
注意:signal 方法在某些情况下可能更可靠,但只支持Unix系统。
5.6 禁用超时
方法 1:使用装饰器
import pytest
@pytest.mark.timeout(0) # 0表示禁用超时
def test_no_timeout():
import time
time.sleep(100) # 不会超时
assert True
方法 2:使用命令行参数
pytest --timeout=0 # 禁用所有超时
6. pytest-mock:Mock 功能增强
6.1 插件简介
pytest-mock 是 pytest 的 mock 插件,它提供了 mocker fixture,让 mock 操作更简单、更符合 pytest 的风格。
6.2 安装方法
pip install pytest-mock
6.3 基本用法
6.3.1 使用 mocker fixture
测试文件:test_mock.py
def test_with_mock(mocker):
"""使用 mocker fixture"""
# Mock 一个函数
mock_func = mocker.patch('module.function')
mock_func.return_value = 'mocked value'
# 使用被 mock 的函数
result = module.function()
assert result == 'mocked value'
mock_func.assert_called_once()
6.4 详细示例
示例 1:Mock 函数
源代码:src/api.py
import requests
def get_user_data(user_id):
"""获取用户数据"""
response = requests.get(f'https://api.example.com/users/{user_id}')
return response.json()
测试文件:tests/test_api.py
import pytest
from src.api import get_user_data
def test_get_user_data(mocker):
"""测试获取用户数据(Mock网络请求)"""
# Mock requests.get
mock_get = mocker.patch('src.api.requests.get')
# 设置返回值
mock_response = mocker.Mock()
mock_response.json.return_value = {'id': 1, 'name': '张三'}
mock_get.return_value = mock_response
# 调用函数
result = get_user_data(1)
# 断言
assert result == {'id': 1, 'name': '张三'}
mock_get.assert_called_once_with('https://api.example.com/users/1')
示例 2:Mock 类方法
源代码:src/database.py
class Database:
def connect(self):
"""连接数据库"""
# 实际数据库连接代码
pass
def query(self, sql):
"""查询数据库"""
# 实际查询代码
pass
测试文件:tests/test_database.py
import pytest
from src.database import Database
def test_database_query(mocker):
"""测试数据库查询(Mock数据库连接)"""
# Mock Database 类的 connect 方法
mock_connect = mocker.patch.object(Database, 'connect')
mock_connect.return_value = None
# Mock query 方法
mock_query = mocker.patch.object(Database, 'query')
mock_query.return_value = [{'id': 1, 'name': 'test'}]
# 使用
db = Database()
db.connect()
result = db.query('SELECT * FROM users')
# 断言
assert result == [{'id': 1, 'name': 'test'}]
mock_connect.assert_called_once()
mock_query.assert_called_once_with('SELECT * FROM users')
示例 3:Mock 环境变量
源代码:src/config.py
import os
def get_api_key():
"""获取API密钥"""
return os.environ.get('API_KEY', 'default_key')
测试文件:tests/test_config.py
import pytest
from src.config import get_api_key
def test_get_api_key(mocker):
"""测试获取API密钥"""
# Mock 环境变量
mocker.patch.dict('os.environ', {'API_KEY': 'test_key_123'})
# 调用函数
result = get_api_key()
# 断言
assert result == 'test_key_123'
示例 4:Mock 时间
源代码:src/utils.py
from datetime import datetime
def get_current_time():
"""获取当前时间"""
return datetime.now()
测试文件:tests/test_utils.py
import pytest
from datetime import datetime
from src.utils import get_current_time
def test_get_current_time(mocker):
"""测试获取当前时间(Mock时间)"""
# Mock datetime.now
fixed_time = datetime(2024, 1, 1, 12, 0, 0)
mocker.patch('src.utils.datetime')
from src.utils import datetime as mock_datetime
mock_datetime.now.return_value = fixed_time
# 调用函数
result = get_current_time()
# 断言
assert result == fixed_time
示例 5:Mock 副作用(Side Effects)
def test_mock_side_effect(mocker):
"""测试 Mock 副作用"""
# 创建一个函数,每次调用返回不同的值
mock_func = mocker.patch('module.function')
mock_func.side_effect = [1, 2, 3]
# 第一次调用返回 1
assert module.function() == 1
# 第二次调用返回 2
assert module.function() == 2
# 第三次调用返回 3
assert module.function() == 3
示例 6:Mock 异常
def test_mock_exception(mocker):
"""测试 Mock 抛出异常"""
mock_func = mocker.patch('module.function')
mock_func.side_effect = ValueError('测试异常')
# 调用应该抛出异常
with pytest.raises(ValueError, match='测试异常'):
module.function()
6.5 Mock 方法说明
6.5.1 mocker.patch()
Mock 一个对象或函数。
mock_obj = mocker.patch('module.object')
6.5.2 mocker.patch.object()
Mock 对象的属性或方法。
mocker.patch.object(SomeClass, 'method')
6.5.3 mocker.patch.dict()
Mock 字典(常用于环境变量)。
mocker.patch.dict('os.environ', {'KEY': 'value'})
6.5.4 mocker.Mock()
创建一个 Mock 对象。
mock_obj = mocker.Mock()
mock_obj.method.return_value = 'value'
6.5.5 mocker.MagicMock()
创建一个 MagicMock 对象(支持更多魔术方法)。
mock_obj = mocker.MagicMock()
6.6 断言方法
# 断言被调用
mock_func.assert_called()
# 断言被调用一次
mock_func.assert_called_once()
# 断言被调用指定次数
mock_func.assert_called_once_with(arg1, arg2)
# 断言调用参数
mock_func.assert_called_with(arg1, arg2)
# 断言调用次数
assert mock_func.call_count == 2
7. pytest-rerunfailures:失败重试
7.1 插件简介
pytest-rerunfailures 允许你自动重试失败的测试用例。这对于处理不稳定的测试(如网络请求、并发问题等)非常有用。
7.2 安装方法
pip install pytest-rerunfailures
7.3 基本用法
7.3.1 全局重试设置
命令:
pytest --reruns 3
说明:所有失败的测试会重试 3 次。
7.3.2 使用装饰器设置单个测试重试
测试文件:test_rerun.py
import pytest
import random
@pytest.mark.flaky(reruns=3) # 重试3次
def test_unstable():
"""不稳定的测试"""
# 模拟不稳定的测试(50%概率失败)
if random.random() < 0.5:
assert False, "随机失败"
assert True
7.4 详细示例
示例 1:全局重试
测试文件:test_network.py
import requests
import random
def test_api_request():
"""测试API请求(可能因为网络问题失败)"""
# 模拟网络请求
response = requests.get('https://api.example.com/data')
assert response.status_code == 200
运行命令:
pytest test_network.py --reruns 3 --reruns-delay 2
参数说明:
--reruns 3:重试3次--reruns-delay 2:每次重试前等待2秒
示例 2:使用装饰器
测试文件:test_flaky.py
import pytest
import random
@pytest.mark.flaky(reruns=5, reruns_delay=1)
def test_flaky_test():
"""不稳定的测试"""
# 模拟不稳定的测试
result = random.random()
assert result > 0.3, f"随机值 {result} 太小"
运行:
pytest test_flaky.py -v
输出示例:
test_flaky.py::test_flaky_test RERUN
test_flaky.py::test_flaky_test RERUN
test_flaky.py::test_flaky_test PASSED
示例 3:只重试特定异常
测试文件:test_specific_exception.py
import pytest
@pytest.mark.flaky(reruns=3, reruns_delay=1, only_rerun=['AssertionError'])
def test_with_specific_exception():
"""只重试 AssertionError"""
# 如果抛出其他异常,不会重试
assert 1 == 2 # 会重试
示例 4:在 pytest.ini 中配置
[pytest]
addopts = --reruns 3 --reruns-delay 2
7.5 高级用法
7.5.1 条件重试
import pytest
@pytest.mark.flaky(
reruns=3,
reruns_delay=1,
condition=lambda result: result.outcome == "failed"
)
def test_conditional_rerun():
"""条件重试"""
assert 1 == 2
7.5.2 重试延迟
@pytest.mark.flaky(reruns=3, reruns_delay=2.5)
def test_with_delay():
"""重试前等待2.5秒"""
assert False
7.6 注意事项
- 不要滥用重试:重试应该用于处理真正不稳定的测试,不应该用来掩盖测试中的bug
- 重试次数:不要设置过多的重试次数,通常2-3次就足够了
- 重试延迟:对于网络请求等,可以设置适当的延迟时间
8. pytest-ordering:控制测试执行顺序
8.1 插件简介
pytest-ordering 允许你控制测试用例的执行顺序。默认情况下,pytest 不保证测试的执行顺序,但有时我们需要按特定顺序执行测试。
8.2 安装方法
pip install pytest-ordering
8.3 基本用法
8.3.1 使用装饰器标记顺序
测试文件:test_ordering.py
import pytest
@pytest.mark.run(order=1)
def test_first():
"""第一个执行"""
print("执行第一个测试")
assert True
@pytest.mark.run(order=2)
def test_second():
"""第二个执行"""
print("执行第二个测试")
assert True
@pytest.mark.run(order=3)
def test_third():
"""第三个执行"""
print("执行第三个测试")
assert True
运行:
pytest test_ordering.py -v -s
输出:
test_ordering.py::test_first 执行第一个测试 PASSED
test_ordering.py::test_second 执行第二个测试 PASSED
test_ordering.py::test_third 执行第三个测试 PASSED
8.4 详细示例
示例 1:基本顺序控制
测试文件:test_login_flow.py
import pytest
@pytest.mark.run(order=1)
def test_login():
"""1. 先登录"""
print("执行登录")
assert True
@pytest.mark.run(order=2)
def test_access_dashboard():
"""2. 访问仪表板"""
print("访问仪表板")
assert True
@pytest.mark.run(order=3)
def test_logout():
"""3. 最后登出"""
print("执行登出")
assert True
示例 2:使用相对顺序
import pytest
@pytest.mark.run(order=-1) # 最后执行
def test_cleanup():
"""清理工作"""
print("清理")
assert True
@pytest.mark.run(order=1) # 第一个执行
def test_setup():
"""准备工作"""
print("准备")
assert True
@pytest.mark.run(order=2) # 中间执行
def test_main():
"""主要测试"""
print("主要测试")
assert True
示例 3:多个测试使用相同顺序
import pytest
@pytest.mark.run(order=1)
def test_setup_1():
"""准备1"""
assert True
@pytest.mark.run(order=1)
def test_setup_2():
"""准备2(与setup_1同顺序,执行顺序不确定)"""
assert True
@pytest.mark.run(order=2)
def test_main():
"""主要测试"""
assert True
8.5 顺序标记说明
8.5.1 正数顺序
@pytest.mark.run(order=1) # 第一个
@pytest.mark.run(order=2) # 第二个
@pytest.mark.run(order=10) # 第十个
8.5.2 负数顺序
@pytest.mark.run(order=-1) # 最后一个
@pytest.mark.run(order=-2) # 倒数第二个
8.5.3 混合使用
@pytest.mark.run(order=1) # 第一个
@pytest.mark.run(order=2) # 第二个
@pytest.mark.run(order=-1) # 最后一个
@pytest.mark.run(order=-2) # 倒数第二个
8.6 注意事项
- 测试独立性:虽然可以控制顺序,但测试应该尽量保持独立
- 不推荐过度使用:如果测试之间有强依赖,考虑使用 fixture 而不是顺序控制
- 性能影响:顺序控制可能会影响并行执行
9. pytest-sugar:美化输出
9.1 插件简介
pytest-sugar 可以美化 pytest 的输出,让测试结果更直观、更易读。它会显示进度条、颜色、表情符号等。
9.2 安装方法
pip install pytest-sugar
9.3 基本用法
安装后无需额外配置,直接运行 pytest 即可看到美化后的输出。
普通输出:
test_example.py ... [100%]
美化输出(pytest-sugar):
test_example.py ✓✓✓ [100%]
9.4 详细示例
示例 1:基本使用
测试文件:test_sugar.py
def test_pass():
"""通过的测试"""
assert True
def test_fail():
"""失败的测试"""
assert False
def test_skip():
"""跳过的测试"""
import pytest
pytest.skip("跳过这个测试")
运行:
pytest test_sugar.py -v
美化输出特点:
- ✓ 表示通过的测试(绿色)
- ✗ 表示失败的测试(红色)
- s 表示跳过的测试(黄色)
- 进度条显示
- 颜色高亮
9.5 功能特点
- 进度条:实时显示测试进度
- 颜色:使用颜色区分不同状态的测试
- 符号:使用直观的符号(✓、✗等)
- 即时反馈:测试完成后立即显示结果
10. pytest-instafail:立即显示失败信息
10.1 插件简介
pytest-instafail 会在测试失败时立即显示失败信息,而不是等到所有测试完成后才显示。这对于长时间运行的测试套件非常有用。
10.2 安装方法
pip install pytest-instafail
10.3 基本用法
命令:
pytest --instafail
10.4 详细示例
示例 1:基本使用
测试文件:test_instafail.py
import time
def test_pass_1():
"""通过的测试1"""
time.sleep(1)
assert True
def test_fail():
"""失败的测试"""
time.sleep(1)
assert False, "这个测试失败了"
def test_pass_2():
"""通过的测试2"""
time.sleep(1)
assert True
不使用 instafail:
pytest test_instafail.py -v
# 需要等待所有测试完成后才显示失败信息
使用 instafail:
pytest test_instafail.py -v --instafail
# 测试失败时立即显示失败信息,不需要等待
10.5 功能特点
- 即时反馈:测试失败时立即显示
- 节省时间:不需要等待所有测试完成
- 便于调试:可以立即看到失败原因
11. pytest-asyncio:异步测试支持
11.1 插件简介
pytest-asyncio 为 pytest 提供异步测试支持,允许你测试异步函数和协程。
11.2 安装方法
pip install pytest-asyncio
11.3 基本用法
11.3.1 使用 pytest.mark.asyncio 装饰器
测试文件:test_async.py
import pytest
import asyncio
@pytest.mark.asyncio
async def test_async_function():
"""测试异步函数"""
await asyncio.sleep(0.1)
assert True
@pytest.mark.asyncio
async def test_async_calculation():
"""测试异步计算"""
result = await asyncio.sleep(0.1)
assert result is None
11.4 详细示例
示例 1:测试异步函数
源代码:src/async_utils.py
import asyncio
async def fetch_data(url):
"""异步获取数据"""
await asyncio.sleep(0.1) # 模拟网络请求
return {"url": url, "data": "test data"}
async def process_data(data):
"""异步处理数据"""
await asyncio.sleep(0.1)
return data.upper() if isinstance(data, str) else data
测试文件:tests/test_async_utils.py
import pytest
from src.async_utils import fetch_data, process_data
@pytest.mark.asyncio
async def test_fetch_data():
"""测试异步获取数据"""
result = await fetch_data("https://example.com")
assert result["url"] == "https://example.com"
assert result["data"] == "test data"
@pytest.mark.asyncio
async def test_process_data():
"""测试异步处理数据"""
result = await process_data("hello")
assert result == "HELLO"
示例 2:测试异步类方法
源代码:src/async_client.py
import asyncio
class AsyncClient:
async def connect(self):
"""异步连接"""
await asyncio.sleep(0.1)
return True
async def send(self, message):
"""异步发送消息"""
await asyncio.sleep(0.1)
return f"Sent: {message}"
测试文件:tests/test_async_client.py
import pytest
from src.async_client import AsyncClient
@pytest.mark.asyncio
async def test_async_client():
"""测试异步客户端"""
client = AsyncClient()
connected = await client.connect()
assert connected is True
result = await client.send("Hello")
assert result == "Sent: Hello"
示例 3:使用 fixture
测试文件:test_async_fixture.py
import pytest
import asyncio
@pytest.fixture
async def async_data():
"""异步 fixture"""
await asyncio.sleep(0.1)
return {"key": "value"}
@pytest.mark.asyncio
async def test_with_async_fixture(async_data):
"""使用异步 fixture"""
assert async_data["key"] == "value"
示例 4:在 pytest.ini 中配置
[pytest]
asyncio_mode = auto
配置说明:
asyncio_mode = auto:自动检测异步测试asyncio_mode = strict:严格模式,必须显式标记
11.5 异步模式说明
11.5.1 auto 模式(默认)
自动检测异步测试函数。
[pytest]
asyncio_mode = auto
11.5.2 strict 模式
必须显式使用 @pytest.mark.asyncio 标记。
[pytest]
asyncio_mode = strict
12. pytest-json-report:生成 JSON 报告
12.1 插件简介
pytest-json-report 可以生成 JSON 格式的测试报告,方便程序化处理和集成到其他工具中。
12.2 安装方法
pip install pytest-json-report
12.3 基本用法
命令:
pytest --json-report --json-report-file=report.json
12.4 详细示例
示例 1:生成 JSON 报告
测试文件:test_json_report.py
def test_pass():
"""通过的测试"""
assert True
def test_fail():
"""失败的测试"""
assert False
def test_skip():
"""跳过的测试"""
import pytest
pytest.skip("跳过")
运行:
pytest test_json_report.py --json-report --json-report-file=report.json
生成的 JSON 报告示例:
{
"created": 1234567890.123,
"duration": 0.123,
"exitcode": 1,
"root": "/path/to/project",
"environment": {
"Python": "3.9.0",
"Platform": "Windows-10"
},
"summary": {
"passed": 1,
"failed": 1,
"skipped": 1,
"total": 3
},
"tests": [
{
"nodeid": "test_json_report.py::test_pass",
"outcome": "passed",
"duration": 0.001
},
{
"nodeid": "test_json_report.py::test_fail",
"outcome": "failed",
"duration": 0.002,
"call": {
"longrepr": "AssertionError: assert False"
}
}
]
}
示例 2:在 CI/CD 中使用
# .github/workflows/test.yml
- name: Run tests
run: |
pytest --json-report --json-report-file=report.json
- name: Upload report
uses: actions/upload-artifact@v2
with:
name: test-report
path: report.json
13. 插件组合使用
13.1 常用组合
在实际项目中,通常会组合使用多个插件。以下是一些常见的组合:
组合 1:基础测试套件
pytest
--html=report.html
--self-contained-html
--cov=src
--cov-report=html
--cov-report=term
-v
功能:
- 生成HTML报告
- 统计代码覆盖率
- 详细输出
组合 2:快速开发
pytest
--instafail
--tb=short
-v
功能:
- 立即显示失败
- 简短的错误信息
- 详细输出
组合 3:CI/CD 环境
pytest
-n auto
--html=report.html
--self-contained-html
--cov=src
--cov-report=xml
--cov-report=term
--json-report
--json-report-file=report.json
--junitxml=junit.xml
功能:
- 并行执行
- 多种报告格式
- 适合CI/CD集成
13.2 pytest.ini 完整配置示例
[pytest]
# 基础配置
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
# 输出配置
addopts =
-v
--tb=short
--strict-markers
--html=report.html
--self-contained-html
--cov=src
--cov-report=html
--cov-report=term-missing
--cov-fail-under=80
--instafail
# 超时配置
timeout = 300
timeout_method = thread
# 重试配置
reruns = 2
reruns_delay = 1
# 异步配置
asyncio_mode = auto
# Markers
markers =
slow: 标记慢速测试
integration: 标记集成测试
unit: 标记单元测试
13.3 requirements.txt 示例
pytest>=7.0.0
pytest-html>=3.0.0
pytest-xdist>=2.5.0
pytest-cov>=4.0.0
pytest-timeout>=2.1.0
pytest-mock>=3.10.0
pytest-rerunfailures>=11.0.0
pytest-ordering>=0.6
pytest-sugar>=0.9.6
pytest-instafail>=0.4.2
pytest-asyncio>=0.21.0
pytest-json-report>=1.5.0
14. 插件选择建议
14.1 必装插件
- pytest-html:生成HTML报告,几乎每个项目都需要
- pytest-cov:代码覆盖率统计,保证代码质量
14.2 推荐插件
- pytest-xdist:如果测试用例较多,强烈推荐
- pytest-mock:如果需要Mock,推荐使用
- pytest-timeout:防止测试无限期运行
- pytest-sugar:美化输出,提升开发体验
14.3 按需安装
- pytest-rerunfailures:如果有不稳定的测试
- pytest-ordering:如果需要控制测试顺序(不推荐过度使用)
- pytest-asyncio:如果项目使用异步编程
- pytest-instafail:如果测试套件运行时间较长