Typing in pytest¶
为什么进行类型测试?¶
类型测试提供了显著的优势
可读性: 清晰地定义了预期的输入和输出,提高了可读性,尤其是在复杂或参数化的测试中。
重构: 这是类型测试的主要好处,因为它将极大地帮助重构,让类型检查器指出生产代码和测试中必要的更改,而无需运行完整的测试套件。
对于生产代码,类型标注还有助于捕获一些可能根本无法通过测试捕获的错误(无论覆盖率如何),例如
def get_caption(target: int, items: list[tuple[int, str]]) -> str:
for value, caption in items:
if value == target:
return caption
类型检查器将正确地报错,指出该函数可能返回 None
,但是即使是全覆盖率的测试套件也可能遗漏这种情况
def test_get_caption() -> None:
assert get_caption(10, [(1, "foo"), (10, "bar")]) == "bar"
请注意,上面的代码具有 100% 的覆盖率,但该错误未被捕获(当然,该示例是“显而易见的”,但旨在说明问题)。
在测试套件中使用类型标注¶
要在 pytest 中为 fixtures 添加类型标注,只需将普通类型添加到 fixture 函数即可 – 不需要因为 fixture
装饰器而进行任何特殊操作。
import pytest
@pytest.fixture
def sample_fixture() -> int:
return 38
同样,传递给测试函数的 fixtures 需要使用 fixture 的返回类型进行注解
def test_sample_fixture(sample_fixture: int) -> None:
assert sample_fixture == 38
从类型检查器的角度来看,sample_fixture
实际上是否是由 pytest 管理的 fixture 并不重要,重要的是 sample_fixture
是 int
类型的参数。
同样的逻辑适用于 @pytest.mark.parametrize
@pytest.mark.parametrize("input_value, expected_output", [(1, 2), (5, 6), (10, 11)])
def test_increment(input_value: int, expected_output: int) -> None:
assert input_value + 1 == expected_output
当为接收其他 fixtures 的 fixture 函数添加类型标注时,也适用相同的逻辑
@pytest.fixture
def mock_env_user(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("USER", "TestingUser")
结论¶
将类型标注融入 pytest 测试中可以增强清晰度,改进调试和维护,并确保类型安全。 这些实践造就了一个健壮的、可读的和易于维护的测试套件,该套件能更好地应对未来的变化,并将错误风险降到最低。