文档
Pytest 测试框架完全指南
背景
测试是软件质量的基石。Pytest 把编写测试这件事从「负担」变成「享受」——它极简的语法让你无需继承 TestCase、无需记忆 30 种 assert 方法,只需写普通的 assert 语句。
第 1 章:Fixture 依赖注入
Fixture 是 Pytest 最核心的概念,用于准备测试所需的资源。
import pytest
import sqlite3
@pytest.fixture(scope="module")
def db():
"""模块级别的数据库连接,所有测试共享"""
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (id INTEGER, name TEXT)")
conn.execute("INSERT INTO users VALUES (1, 'Alice')")
yield conn # 测试执行到这里
conn.close() # teardown 清理
def test_query_user(db):
cursor = db.execute("SELECT name FROM users WHERE id=1")
assert cursor.fetchone() == ("Alice",)
Fixture Scope
| scope | 生命周期 |
|---|---|
| function(默认) | 每个测试函数 |
| class | 每个测试类 |
| module | 每个 .py 文件 |
| session | 整个 pytest 运行期间 |
第 2 章:Mock 与 Monkeypatch
# 使用 monkeypatch 替换外部依赖
def test_fetch_data(monkeypatch):
def mock_get(url, *args, **kwargs):
class FakeResponse:
status_code = 200
def json(self):
return {"data": "mocked"}
return FakeResponse()
monkeypatch.setattr("requests.get", mock_get)
from myapp import fetch_user_data
assert fetch_user_data(1) == {"data": "mocked"}
第 3 章:conftest.py 共享 Fixture
# conftest.py — 放在项目根目录,自动被所有测试文件共享
import pytest
from myapp import create_app
@pytest.fixture
def app():
app = create_app(testing=True)
yield app
@pytest.fixture
def client(app):
return app.test_client()
# 任何 test_*.py 都可以直接使用 app 和 client fixture
第 4 章:覆盖率与并行
# 生成覆盖率报告
pytest --cov=myapp --cov-report=html tests/
# 并行执行(需要 pytest-xdist)
pytest -n auto tests/
# 组合使用
pytest -n 4 --cov=myapp --cov-report=term tests/
第 5 章:标记(Mark)与分组
# 给测试打标记
@pytest.mark.slow
def test_heavy_computation():
pass
@pytest.mark.integration
def test_database_write():
pass
# 按标记运行
# pytest -m "slow" # 只运行 slow
# pytest -m "not integration" # 跳过 integration
# pytest -m "slow and not integration"
在 pytest.ini 中注册标记:
[pytest]
markers =
slow: 标记慢速测试
integration: 标记集成测试
思考题
- fixture 的
scope="session"和scope="function"分别适合管理什么类型的资源? - monkeypatch 和 unittest.mock 的区别是什么?什么时候该用哪种?
- 如何在 CI/CD 中设置最低覆盖率阈值并阻止低覆盖率合并?