跳转到内容

Python测试

来源: everything-claude-code 技能库

python-testing 是使用 pytest 进行 Python 应用全面测试的技能。它涵盖 TDD 方法论、最佳实践、Fixtures、Mocking、参数化和覆盖率要求。

始终遵循 TDD 循环:

graph TD
  A[RED: 写失败的测试] --> B[GREEN: 写最小代码]
  B --> C[REFACTOR: 重构优化]
  C --> A
  1. 红色 (RED) - 为期望行为编写一个失败的测试
  2. 绿色 (GREEN) - 编写最少的代码让测试通过
  3. 重构 (REFACTOR) - 保持测试通过的情况下改进代码
# 步骤 1: 编写失败的测试 (RED)
def test_add_numbers():
result = add(2, 3)
assert result == 5
# 步骤 2: 编写最小实现 (GREEN)
def add(a, b):
return a + b
# 步骤 3: 重构 (REFACTOR)
目标说明
目标80%+ 代码覆盖率
关键路径必须 100% 覆盖
工具pytest —cov
Terminal window
pytest --cov=mypackage --cov-report=term-missing --cov-report=html
import pytest
def test_addition():
"""测试基本加法。"""
assert 2 + 2 == 4
def test_string_uppercase():
"""测试字符串大写。"""
text = "hello"
assert text.upper() == "HELLO"
def test_list_append():
"""测试列表追加。"""
items = [1, 2, 3]
items.append(4)
assert 4 in items
assert len(items) == 4
# 相等断言
assert result == expected
# 不相等断言
assert result != unexpected
# 真值断言
assert result is True
assert result is None
# 浮点数断言
assert abs(result - expected) < 0.0001
# 异常断言
with pytest.raises(ValueError):
function()
# 近似断言
assert math.isclose(result, expected, rel_tol=1e-9)

Fixtures 是 pytest 最强大的功能之一,用于提供测试数据和 setup/teardown 逻辑。

import pytest
@pytest.fixture
def user():
"""创建测试用户。"""
return {"name": "test", "email": "test@example.com"}
def test_user_name(user):
assert user["name"] == "test"
作用域说明示例
function每个测试函数执行一次默认
class每个测试类执行一次@pytest.fixture(scope=“class”)
module每个模块执行一次@pytest.fixture(scope=“module”)
session整个测试会话执行一次@pytest.fixture(scope=“session”)
@pytest.fixture
def db_connection():
"""数据库连接 fixture。"""
return create_connection()
@pytest.fixture
def user_repository(db_connection):
"""依赖数据库连接的 repository。"""
return UserRepository(db_connection)
@pytest.fixture
def user_factory():
"""用户工厂 fixture。"""
def _create_user(name):
return {"name": name, "id": generate_id()}
return _create_user
def test_create_user(user_factory):
user = user_factory("张三")
assert user["name"] == "张三"

使用 unittest.mock 或 pytest-mock 进行模拟。

from unittest.mock import Mock, patch
def test_api_call():
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"data": "test"}
with patch('requests.get', return_value=mock_response):
result = api.get_data()
assert result == {"data": "test"}
# 模拟整个模块
@patch('module.function')
def test_function(mock_func):
mock_func.return_value = 42
assert module.function() == 42
# 模拟上下文管理器
@patch('builtins.open', create=True)
def test_file_read(mock_open):
mock_open.return_value.__enter__.return_value.read.return_value = "file content"
# 测试代码

Spy 允许你监视函数的调用,但不替换实现:

def test_spy_example(records):
spy = pytest.spy(records, 'save')
records.save()
spy.assert_called_once()

参数化允许用不同输入运行同一测试:

@pytest.mark.parametrize("input,expected", [
(2, 4),
(3, 9),
(4, 16),
(5, 25),
])
def test_square(input, expected):
assert input ** 2 == expected
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])
def test_add(a, b, expected):
assert a + b == expected
@pytest.mark.parametrize("a", [1, 2, 3])
@pytest.mark.parametrize("b", [10, 20])
def test_combinations(a, b):
# 将运行 3 * 2 = 6 次
assert isinstance(a + b, int)
✅ 正确做法:
1. 先写一个失败的测试 (RED)
2. 写最少的代码让测试通过 (GREEN)
3. 重构代码确保测试仍然通过 (REFACTOR)
4. 提交测试和代码
❌ 错误做法:
1. 先写代码,然后写测试
2. 测试覆盖率低就提交
3. 不运行完整的测试套件
✅ 正确做法:
1. 识别外部依赖(API、数据库、文件系统)
2. 使用 @patch 或 Mock 替换
3. 验证调用参数和次数
4. 测试边界条件和异常
❌ 错误做法:
1. 依赖真实的外部服务
2. Mock 整个类而不是具体方法
3. 不验证 mock 的调用
✅ 正确做法:
1. Fixture 应该是纯函数,无副作用
2. 使用合适的 scope 减少重复创建
3. 清晰的 fixture 命名
4. 依赖关系显式声明
❌ 错误做法:
1. Fixture 中包含测试逻辑
2. Fixture 之间有隐藏的依赖
3. Scope 设置过大导致测试污染
Terminal window
# 运行所有测试
pytest
# 运行特定文件
pytest tests/test_api.py
# 运行特定测试
pytest tests/test_api.py::test_get_user
# 显示详细输出
pytest -v
# 停在第一个失败
pytest -x
# 显示局部变量
pytest -l
# 生成覆盖率报告
pytest --cov=mypackage --cov-report=term-missing
# 运行上次失败的测试
pytest --lf
# 并行执行
pytest -n auto
# 只运行失败的测试
pytest --failed-first

跳过 TDD

错误:先写代码后写测试 正确:遵循 RED-GREEN-REFACTOR 循环

Mock 过度

错误:Mock 太多实现细节 正确:只 Mock 外部依赖

Fixture 滥用

错误:Fixture 中包含复杂逻辑 正确:保持 Fixture 简单纯净

忽略覆盖率

错误:覆盖率低就提交 正确:80%+ 覆盖率,关键路径 100%

何时使用

  • 编写新的 Python 代码
  • 设计测试套件
  • 审查测试覆盖率
  • 设置测试基础设施

何时不用

  • 一次性脚本
  • 简单原型
  • 文档生成

关键要点

  • TDD 循环
  • 80%+ 覆盖率
  • Fixture 复用
  • 正确的 Mock 粒度

常见错误

  • 后补测试
  • Mock 过度
  • 忽略边界条件
  • 不测试异常

Python 测试技能强调:

  1. 测试即文档 - 测试应该是代码的可执行文档
  2. 快速反馈 - 测试应该快速运行,提供即时反馈
  3. 可维护性 - 测试代码也需要良好的设计和维护
  4. 可靠性 - 测试应该稳定可靠,不 flaky
特性pytestunittest
语法函数式类式
Fixture原生支持需继承
参数化原生支持需插件
Mock需安装原生支持
社区活跃标准库
  • 测试文件名:test_*.py*_test.py
  • 测试函数:test_*
  • 测试类:Test*
  • 每个测试一个断言(尽量)
  • 测试之间相互独立
  • 使用有意义的测试名称
技能关系
tdd-workflowTDD 工作流基础
python-patternsPython 编程模式
code-review代码审查
  1. 安装 pytest - pip install pytest pytest-cov pytest-mock
  2. 创建测试文件 - test_*.py
  3. 编写测试 - 遵循 TDD
  4. 运行测试 - pytest
  5. 检查覆盖率 - pytest --cov

官方原文: python-testing SKILL.md


💡 提示:测试是最好的文档,好的测试能让代码自己说话。