From python-skills
使用 pytest 编写高质量单元测试的通用规范。涵盖测试结构、fixture 使用、断言风格、 参数化测试、异步测试和覆盖率目标。在创建或修改测试文件时使用此 skill。
npx claudepluginhub zengbin93/python_coding_skills --plugin python-skillsThis skill uses the workspace's default tool permissions.
- 测试应当**快速、独立、可重复、自验证**(FIRST 原则)
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
| 要测试什么 | 推荐方式 |
|---|---|
| 函数/方法的返回值 | 基本单元测试,AAA 模式 |
| 函数的行为(调用、副作用) | Mock 验证函数调用和参数 |
| 数据库/API 等外部依赖 | Fixtures + Mock 外部服务 |
| 异步代码 | pytest-asyncio,配置 asyncio_mode = "auto" |
| 多种输入组合 | @pytest.mark.parametrize 参数化测试 |
| 异常情况 | pytest.raises |
| 共享测试数据或资源 | Fixtures(按作用域选择) |
project/
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── calculator.py
│ └── user_service.py
└── tests/
├── conftest.py # 共享 fixture
├── test_calculator.py
└── test_user_service.py
注意:tests/ 目录不需要 __init__.py 文件。
test_ 开头:test_calculator.pytest_ 开头:def test_add_returns_correct_sum()test_ 前缀:helpers.py、factories.py遵循 test_<功能>_<场景>_<预期结果> 模式:
# ✅ 清晰的命名
def test_divide_by_zero_raises_value_error():
def test_create_user_with_valid_data_returns_user():
def test_login_with_invalid_password_returns_401():
# ❌ 模糊的命名
def test_divide():
def test_user():
def test_login2():
def test_calculate_discount_returns_correct_price():
# Arrange(准备)
price = 100.0
rate = 0.2
# Act(执行)
result = calculate_discount(price, rate)
# Assert(验证)
assert result == 80.0
# tests/conftest.py
import pytest
from mypackage.database import Database
@pytest.fixture
def db():
"""提供测试数据库连接,测试结束后自动清理。"""
database = Database(":memory:")
database.create_tables()
yield database
database.close()
@pytest.fixture
def sample_user():
return {"id": 1, "name": "张三", "email": "zhang@example.com"}
def test_save_user_persists_to_database(db, sample_user):
db.save_user(sample_user)
retrieved = db.get_user(sample_user["id"])
assert retrieved["name"] == sample_user["name"]
@pytest.fixture(scope="session") # 整个测试会话共享一次
@pytest.fixture(scope="module") # 每个模块共享一次
@pytest.fixture(scope="class") # 每个测试类共享一次
@pytest.fixture(scope="function") # 默认,每个测试函数独立
使用 @pytest.mark.parametrize 避免重复测试代码:
import pytest
@pytest.mark.parametrize("price,rate,expected", [
(100.0, 0.0, 100.0), # 无折扣
(100.0, 0.5, 50.0), # 五折
(100.0, 1.0, 0.0), # 全额折扣
(0.0, 0.3, 0.0), # 零价格
])
def test_calculate_discount(price, rate, expected):
assert calculate_discount(price, rate) == expected
@pytest.mark.parametrize("invalid_rate", [-0.1, 1.1, 2.0])
def test_calculate_discount_with_invalid_rate_raises(invalid_rate):
with pytest.raises(ValueError, match="折扣率必须在"):
calculate_discount(100.0, invalid_rate)
def test_divide_by_zero_raises_zero_division_error():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
# 验证异常消息
def test_invalid_email_raises_with_message():
with pytest.raises(ValueError, match="邮箱格式不正确"):
validate_email("not-an-email")
# 捕获并检查异常对象
def test_custom_exception_has_correct_code():
with pytest.raises(AppError) as exc_info:
raise_app_error(code=404)
assert exc_info.value.code == 404
monkeypatch — 简单替换,适合修改环境变量、配置等:
def test_with_monkeypatch(monkeypatch):
monkeypatch.setenv("API_KEY", "test_key")
monkeypatch.setattr("module.function", mock_function)
pytest-mock (mocker) — 强大的 mock 功能,适合验证调用:
def test_with_mocker(mocker):
mock_func = mocker.patch("module.function")
mock_func.return_value = "result"
mock_func.assert_called_with("args")
✅ 正确:在使用的地方 patch(patch 被测试代码所引用的路径)
mocker.patch("myapp.utils.helper.function") # myapp 使用 function
❌ 错误:在定义的地方 patch
from myapp.utils import helper
mocker.patch("helper.function") # 不会生效
from unittest.mock import MagicMock, patch
def test_send_email_calls_smtp_once():
with patch("mypackage.email.smtplib.SMTP") as mock_smtp:
mock_instance = mock_smtp.return_value.__enter__.return_value
send_welcome_email("user@example.com")
mock_instance.sendmail.assert_called_once()
def test_get_user_calls_api_with_correct_id():
mock_client = MagicMock()
mock_client.get.return_value = {"id": 1, "name": "李四"}
service = UserService(client=mock_client)
user = service.get_user(1)
mock_client.get.assert_called_once_with("/users/1")
assert user["name"] == "李四"
import pytest
# pytest.ini 或 pyproject.toml 中配置:
# [tool.pytest.ini_options]
# asyncio_mode = "auto"
async def test_async_fetch_returns_data():
result = await fetch_data("https://api.example.com/data")
assert result is not None
async def test_async_operation_with_fixture(db):
await db.async_save({"key": "value"})
result = await db.async_get("key")
assert result == "value"
# 标记慢速测试
@pytest.mark.slow
def test_large_data_processing():
...
# 标记需要网络的测试
@pytest.mark.network
def test_external_api_integration():
...
# 跳过测试
@pytest.mark.skip(reason="功能尚未实现")
def test_future_feature():
...
# 条件跳过
@pytest.mark.skipif(sys.platform == "win32", reason="不支持 Windows")
def test_unix_only_feature():
...
在 pyproject.toml 中注册自定义 marker:
[tool.pytest.ini_options]
markers = [
"slow: 标记为慢速测试,可用 -m 'not slow' 跳过",
"network: 需要网络连接的集成测试",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
addopts = "-v --tb=short"
[tool.coverage.run]
source = ["src"]
omit = ["tests/*", "**/__init__.py"]
[tool.coverage.report]
fail_under = 80
show_missing = true
# 运行所有测试
pytest
# 运行指定文件
pytest tests/test_calculator.py
# 运行指定测试函数
pytest tests/test_calculator.py::test_add_returns_correct_sum
# 按标记运行
pytest -m slow
pytest -m "not slow"
# 带覆盖率报告
pytest --cov=src --cov-report=term-missing
# 失败时立即停止
pytest -x
# 并行执行(需安装 pytest-xdist)
pytest -n auto
test_<module>.pypytest --covpytest -vpytest -v 或 pytest -vvprint()(提交前删除)pytest tests/test_file.py::test_functionpytest --tb=long本 skill 包含以下详细参考文档(位于 references/ 目录):
Fixtures 完全指南:基础 fixtures、作用域、参数化、依赖关系、conftest.py、内置 fixtures(tmp_path、capsys、caplog、monkeypatch)、Async fixtures
Mocking 和 Patching 完全指南:monkeypatch 用法、pytest-mock 使用、Mock 对象配置和验证、常见 mock 模式(API、数据库、文件系统)、Patch 策略、常见陷阱
异步测试完全指南:pytest-asyncio 配置(auto/strict 模式)、基本异步测试、异步 fixtures、Loop scope 控制、Mock 异步操作
常用测试模式和示例:测试组织、参数化测试、异常测试、标记和跳过、测试配置、数据驱动测试、集成测试模式、边界条件测试
测试最佳实践和规范:FIRST 原则、AAA/Given-When-Then 模式、覆盖率配置、Fixture/Mock/断言最佳实践、性能考虑、CI/CD 集成
print 辅助调试,完成后及时删除;不要将调试输出留在最终提交的代码中在提交测试代码前确认:
print 语句