目录
1. 安装与基础配置
安装方法
版本检查
配置文件
2. 编写测试函数
基本结构
断言机制
3. 测试执行与报告
基本执行方式
常用命令行选项
测试报告
4. 测试组织与管理
测试类
模块化测试
5. 高级测试功能
Fixtures 详解
参数化测试
异常测试进阶
6. 测试控制与标记
跳过测试
标记测试
7. 插件生态系统
常用插件
8. 最佳实践
9. 完整示例项目
10. 学习资源
pytest 是 Python 生态中最流行、功能最强大的测试框架之一,它提供了简洁优雅的语法和丰富灵活的测试功能。下面我们将详细介绍 pytest 的核心概念和实用技巧。
使用 pip 安装最新版本 pytest:
pip install pytest
对于开发环境,建议同时安装常用插件:
pip install pytest pytest-cov pytest-xdist pytest-mock
安装完成后可以检查版本:
pytest --version
pytest 支持通过 pytest.ini
文件进行配置,例如:
[pytest]
addopts = -v --tb=short
python_files = test_*.py
python_classes = Test*
python_functions = test_*
测试函数应遵循以下命名约定:
test_*.py
或 *_test.py
test_*()
Test*
示例测试文件 test_calculator.py
:
def add(a, b):
"""加法函数"""
return a + b
def test_add_integers():
"""测试整数加法"""
assert add(2, 3) == 5
assert add(-1, 1) == 0
def test_add_floats():
"""测试浮点数加法"""
assert add(0.1, 0.2) == pytest.approx(0.3)
pytest 支持所有标准 Python 断言,并提供增强功能:
# 数值比较
assert result == expected
assert abs(result - expected) < 0.001
# 容器测试
assert item in collection
assert collection1 == collection2
# 异常测试
with pytest.raises(ValueError):
function_that_raises()
# 使用 pytest.approx 处理浮点数比较
assert 0.1 + 0.2 == pytest.approx(0.3)
# 运行所有测试
pytest
# 运行特定测试文件
pytest test_module.py
# 运行特定测试函数
pytest test_module.py::test_function
# 运行特定测试类
pytest test_module.py::TestClass
选项 | 说明 |
---|---|
-v |
详细输出 |
-x |
遇到第一个失败就停止 |
--maxfail=n |
允许最多 n 次失败 |
-k "expression" |
按名称筛选测试 |
--durations=n |
显示最慢的 n 个测试 |
--lf |
只运行上次失败的测试 |
--ff |
先运行失败的测试 |
--cov |
生成覆盖率报告 |
生成 HTML 测试报告:
pytest --html=report.html
生成 JUnit 格式报告:
pytest --junitxml=report.xml
将相关测试组织在类中:
class TestMathOperations:
"""测试数学运算"""
def test_addition(self):
assert 1 + 1 == 2
def test_subtraction(self):
assert 5 - 3 == 2
def test_multiplication(self):
assert 2 * 3 == 6
可以使用 Python 包结构组织测试:
tests/
├── __init__.py
├── unit/
│ ├── test_math.py
│ └── test_string.py
└── integration/
├── test_database.py
└── test_api.py
Fixtures 提供测试所需资源并处理清理工作:
import pytest
@pytest.fixture(scope="module")
def database_connection():
"""创建数据库连接"""
conn = create_db_connection()
yield conn # 测试函数会在此处运行
conn.close() # 测试完成后清理
@pytest.fixture
def temporary_file(tmp_path):
"""使用内置 tmp_path fixture 创建临时文件"""
file = tmp_path / "test.txt"
file.write_text("test data")
return file
def test_database_query(database_connection):
result = database_connection.query("SELECT * FROM users")
assert len(result) > 0
def test_file_operations(temporary_file):
assert temporary_file.read_text() == "test data"
使用 @pytest.mark.parametrize
测试多组数据:
import pytest
@pytest.mark.parametrize(
"input,expected",
[
("3+5", 8),
("2*4", 8),
("6/2", 3),
("10-2", 8),
]
)
def test_eval(input, expected):
assert eval(input) == expected
import pytest
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError) as excinfo:
1 / 0
assert str(excinfo.value) == "division by zero"
@pytest.mark.parametrize(
"value,exception",
[
(None, TypeError),
("string", ValueError),
(-1, ValueError)
]
)
def test_sqrt(value, exception):
import math
with pytest.raises(exception):
math.sqrt(value)
import sys
import pytest
@pytest.mark.skip(reason="功能尚未实现")
def test_unimplemented():
assert False
@pytest.mark.skipif(sys.version_info < (3, 8), reason="需要 Python 3.8+")
def test_new_syntax():
assert (x := 5) == 5
@pytest.mark.slow
def test_long_running():
import time
time.sleep(10)
assert True
@pytest.mark.integration
def test_database_integration():
assert database_is_available()
运行标记的测试:
pytest -m "slow or integration"
pytest 的强大之处在于其丰富的插件生态系统:
pytest-cov:代码覆盖率测试
pytest --cov=myproject --cov-report=html
pytest-xdist:并行测试
pytest -n 4 # 使用4个CPU核心
pytest-mock:模拟对象
def test_api_call(mocker):
mocker.patch("requests.get", return_value={"status": "ok"})
assert call_api() == "ok"
pytest-django:Django项目测试
pytest-asyncio:异步测试
pytest-timeout:测试超时控制
以下是一个典型的项目结构和使用 pytest 的示例:
myproject/
├── src/
│ └── calculator.py
├── tests/
│ ├── __init__.py
│ ├── unit/
│ │ ├── test_math.py
│ │ └── test_operations.py
│ └── integration/
│ └── test_api.py
├── pytest.ini
└── requirements.txt
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("Cannot divide by zero")
return a / b
tests/unit/test_math.py
:
import pytest
from src.calculator import Calculator
@pytest.fixture
def calculator():
return Calculator()
class TestCalculator:
def test_add(self, calculator):
assert calculator.add(2, 3) == 5
def test_subtract(self, calculator):
assert calculator.subtract(5, 3) == 2
def test_multiply(self, calculator):
assert calculator.multiply(2, 3) == 6
def test_divide(self, calculator):
assert calculator.divide(6, 3) == 2
def test_divide_by_zero(self, calculator):
with pytest.raises(ValueError, match="Cannot divide by zero"):
calculator.divide(1, 0)
通过掌握 pytest 的这些功能,你将能够为 Python 项目编写高效、可维护的测试代码,显著提高代码质量和开发效率。