关于 pytest-advanced
面向需要构建可维护测试套件的 Python 开发者,介绍 pytest 的进阶用法,包括 fixture 层级、复杂参数化和插件配置。还涵盖异步测试、并行执行及 CI 集成。
主要功能
- 01按作用域和工厂模式组织 fixture
- 02复杂参数化与标记筛选
- 03配置 pytest 插件
- 04异步测试与 mock
- 05并行执行及 CI 集成
适用场景
- 01为多模块测试套件共享 fixture
- 02测试多种输入或数据库后端
- 03并行运行测试并快速定位失败
- 04为 CI 输出覆盖率和测试结果
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ laurigates/ claude-plugins --skill pytest-advanced安装后在 Claude Code 中输入/pytest-advanced确认已加载。
附带文件REFERENCE.md
详细介绍
高级 Pytest 模式
用于构建健壮、易维护测试套件的高级 pytest 功能。
何时使用此技能
| 在以下情况下使用此技能…… | 在以下情况下改用 python-testing…… |
|---|---|
| 编写带作用域/工厂的 fixture | 了解基本测试结构 |
| 使用复杂数据进行参数化 | 使用简单的 assert 模式 |
| 设置 pytest 插件(cov、xdist) | 运行现有测试 |
| 组织 conftest.py 层级结构 | 了解测试发现基础知识 |
| 使用 pytest-asyncio 进行异步测试 | 编写同步单元测试 |
安装
# Core + common plugins
uv add --dev pytest pytest-cov pytest-asyncio pytest-xdist pytest-mock
配置(pyproject.toml)
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = [
"-v",
"--strict-markers",
"--tb=short",
"-ra",
"--cov=src",
"--cov-report=term-missing",
"--cov-fail-under=80",
]
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"integration: marks tests as integration tests",
]
asyncio_mode = "auto"
[tool.coverage.run]
branch = true
source = ["src"]
[tool.coverage.report]
exclude_lines = ["pragma: no cover", "if TYPE_CHECKING:", "@abstractmethod"]
Fixture
作用域与生命周期
import pytest
from typing import Generator
# function (default) - fresh per test
@pytest.fixture
def db() -> Generator[Database, None, None]:
database = Database(":memory:")
database.create_tables()
yield database
database.close()
# session - shared across all tests
@pytest.fixture(scope="session")
def app():
return create_app("testing")
# autouse - applies automatically
@pytest.fixture(autouse=True)
def reset_state():
clear_cache()
yield
| 作用域 | 生命周期 |
|---|---|
function | 每个测试(默认) |
class | 每个测试类 |
module | 每个测试文件 |
session | 整个测试运行期间 |
参数化 Fixture
@pytest.fixture(params=["sqlite", "postgres", "mysql"])
def database_backend(request) -> str:
return request.param # Test runs 3 times
# Indirect parametrization
@pytest.fixture
def user(request) -> User:
return User(**request.param)
@pytest.mark.parametrize("user", [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
], indirect=True)
def test_user_validation(user: User):
assert user.name
工厂模式
@pytest.fixture
def user_factory() -> Callable[[str], User]:
created: list[User] = []
def _create(name: str, **kwargs) -> User:
user = User(name=name, **kwargs)
created.append(user)
return user
yield _create
for u in created:
u.delete()
标记
# Built-in markers
@pytest.mark.skip(reason="Not implemented")
@pytest.mark.skipif(sys.version_info < (3, 12), reason="Requires 3.12+")
@pytest.mark.xfail(reason="Known bug #123")
@pytest.mark.timeout(10)
# Parametrize
@pytest.mark.parametrize("input,expected", [
pytest.param(2, 4, id="two"),
pytest.param(3, 9, id="three"),
pytest.param(-2, 4, id="negative"),
])
def test_square(input: int, expected: int):
assert input ** 2 == expected
# Run by marker
pytest -m unit # Only unit tests
pytest -m "not slow" # Skip slow tests
pytest -m "integration and not slow" # Combine markers
主要插件
| 插件 | 用途 | 主要命令 |
|---|---|---|
| pytest-cov | 覆盖率 | pytest --cov=src --cov-report=term-missing |
| pytest-xdist | 并行 | pytest -n auto |
| pytest-asyncio | 异步测试 | 配置中的 asyncio_mode = "auto" |
| pytest-mock | 模拟 | mocker fixture |
| pytest-timeout | 超时 | @pytest.mark.timeout(10) |
异步测试(pytest-asyncio)
@pytest.mark.asyncio
async def test_async_function():
result = await fetch_data()
assert result is not None
@pytest.fixture
async def async_client() -> AsyncGenerator[AsyncClient, None]:
async with AsyncClient() as client:
yield client
模拟(pytest-mock)
def test_with_mock(mocker):
mock_api = mocker.patch("myapp.external.api_call")
mock_api.return_value = {"data": "test"}
result = my_function()
assert result["data"] == "test"
mock_api.assert_called_once()
运行测试
# Execution
pytest # All tests
pytest tests/test_models.py::test_user # Specific test
pytest -k "user and not slow" # Pattern matching
# Parallel
pytest -n auto # All CPUs
pytest -n 4 # 4 workers
# Coverage
pytest --cov=src --cov-report=html --cov-report=term-missing
# Failed tests
pytest --lf # Last failed only
pytest --ff # Failed first
pytest -x # Stop on first failure
pytest --maxfail=3 # Stop after 3
# Debugging
pytest -x --pdb # Debug on failure
pytest -s # Show print output
pytest --collect-only # Dry run
CI 集成
# .github/workflows/test.yml
- name: Run tests
run: |
uv run pytest \
--cov=src \
--cov-report=xml \
--cov-report=term-missing \
--junitxml=test-results.xml
Agentic 优化
| 场景 | 命令 |
|---|---|
| 快速检查 | pytest -x --tb=short -q |
| 快速失败 | pytest -x --maxfail=1 --tb=short |
| 并行快速运行 | pytest -n auto -x --tb=short -q |
| 指定测试 | pytest tests/test_foo.py::test_bar -v |
| 按标记筛选 | pytest -m "not slow" -x --tb=short |
| 覆盖率检查 | pytest --cov=src --cov-fail-under=80 -q |
| CI 模式 | pytest --junitxml=results.xml --cov-report=xml -q |
| 上次失败的测试 | pytest --lf --tb=short |
| 调试 | pytest -x --pdb -s |
有关 conftest.py 层级结构、异步测试、测试组织和常见模式的详细说明,请参阅 REFERENCE.md。
评论
支持 Markdown