pytest-mypy默认不检查test_*.py文件,因其被mypy视为“运行时脚本”而跳过;需显式配置follow_imports=normal、--mypy-pyargs tests/并设置files=["src/", "tests/"]才能启用类型检查。

pytest-mypy 为什么不能直接检查测试文件?
默认情况下,pytest-mypy 只会检查被 import 的模块,而不会主动扫描 test_*.py 文件——哪怕它们有类型注解。这是因为 mypy 默认把测试文件当作“运行时脚本”,跳过类型检查。你运行 pytest --mypy 却没报错,很可能根本没检查到你的测试代码。
解决办法是显式告诉 mypy:这些测试文件也要当“模块”对待:
- 在项目根目录加
mypy.ini或pyproject.toml,配置[mypy]段并启用follow_imports = normal - 更重要的是,在
pytest.ini或命令行中传参:--mypy-ignore-missing-imports=False(避免因导入未检查而静默跳过) - 确保测试文件路径被包含在 mypy 的
files列表里,例如:pytest --mypy --mypy-pyargs tests/
如何让 pytest-mypy 真正检查 test_*.py 里的类型错误?
关键不是改 pytest 插件,而是让 mypy 能“看到”测试文件。最可靠的方式是用 --mypy-pyargs 显式指定路径,并配合 mypy 配置排除干扰:
- 运行命令:
pytest --mypy --mypy-pyargs tests/(注意末尾斜杠,确保递归进入子目录) - 在
pyproject.toml中补充 mypy 配置:[tool.mypy] files = ["src/", "tests/"] disallow_untyped_defs = true disallow_incomplete_defs = true
- 如果测试里用了 mock 或 fixture,可能触发
error: Cannot assign to a method这类误报,此时需加局部忽略:# type: ignore[attr-defined]
常见报错和绕不过去的坑
pytest-mypy 本质是调用 mypy,所以所有 mypy 的限制它都有。最容易卡住的几个点:
立即学习“Python免费学习笔记(深入)”;
-
error: Skipping analyzing 'xxx': found module but no type hints or external stubs—— 表示 mypy 找到了模块但没看到类型注解,不是警告,是直接跳过。解决方案:给被测模块加__all__或至少一个def func() -> None:注解,触发 mypy 加载 - fixture 返回值无类型 → mypy 推导为
Any→ 后续断言不报错。必须给 fixture 加返回类型,例如:@pytest.fixture -> list[str]: - 使用
typing.Any或cast时,mypy 不会报错,但会掩盖真实问题。建议只在极少数无法推导的场景用,且加注释说明原因
要不要在 CI 里用 pytest-mypy 检查测试代码?
可以,但得接受它比纯 mypy 慢——因为 pytest 会先收集测试、再启动 mypy 子进程。更轻量的做法是单独跑:mypy --files tests/ src/。
如果你坚持用 pytest --mypy,注意两点:
- CI 中禁用
--tb=short类参数,否则 mypy 报错堆栈会被截断 - 不要依赖
--mypy-exit-code的默认值;明确设为--mypy-exit-code=1,否则类型错误可能被当成 pytest 成功通过
真正难的不是配置,是让每个 fixture、每个 mock、每个 assert 都保持类型可推导。这需要团队对 typing 有一致理解,而不是靠工具兜底。


















