如何用mypy实现类型不兼容场景的自动化检查?
问题背景与需求
我正在开发一个小型Python库,其中包含一个示例函数:
def takes_a_str(x: str) -> str: if x.startswith("."): raise RuntimeError("Must not start with '.'") return x + ";"
针对这个函数,我已经能完成以下测试:
- 运行时功能测试:用pytest验证正常场景和异常场景的行为
# 正常场景断言 assert takes_a_str('x') == 'x;' # 异常场景断言 with pytest.raises(RuntimeError): takes_a_str('.') - 正向类型测试:编写专门的类型检查函数,运行mypy或pyright确保无类型错误
def check_types() -> None: x: str = takes_a_str("")
但我需要进一步验证类型提示不过于宽松——也就是要确保当传入错误类型时,类型检查器能准确报错。比如下面的代码应该触发类型错误:
def should_fail_type_checking() -> None: x: dict = takes_a_str("") # 错误:返回值为str,不能赋值给dict类型变量 takes_a_str(2) # 错误:参数为int,不符合函数要求的str类型
现在的问题是,手动运行mypy检查这类用例效率极低:如果有20个类似的负向用例,我没法快速确认所有预期错误都已触发,还容易漏掉混杂的其他无关错误。
有没有办法实现类似pytest.raises()的效果——让类型检查器仅在指定位置出现类型不匹配时,才算测试通过?
解决方案
有几个实用的方案能帮你实现这种「断言类型检查必须报错」的自动化测试,效果类似pytest.raises():
1. 用pytest-mypy-plugin整合mypy测试
这个插件能把mypy的静态类型检查直接融入pytest的测试流程,还能指定某段代码必须触发特定类型错误。
操作步骤:
- 安装插件:
pip install pytest-mypy - 在测试文件中,给需要检查的负向用例加上
type: ignore注释,明确标记预期的错误类型:import pytest from your_module import takes_a_str def test_negative_type_checks(): # 预期赋值类型不匹配,标记忽略assignment错误 x: dict = takes_a_str("") # type: ignore[assignment] # 预期参数类型不匹配,标记忽略arg-type错误 takes_a_str(2) # type: ignore[arg-type] - 运行测试时带上
--mypy-strict参数,这样只有你标记了预期错误的地方会被忽略,任何未标记的类型错误都会导致测试失败:pytest --mypy --mypy-strict tests/
这样就能把类型测试和普通功能测试一起执行,自动化验证所有负向用例都触发了预期的类型错误。
2. 用pyright的注释+配置实现精准检查
如果你习惯使用pyright作为类型检查器,也可以通过注释标记预期错误,配合严格配置确保没有漏网之鱼。
操作步骤:
- 在测试代码中给预期错误的行加上pyright专属注释:
def should_fail_type_checking() -> None: x: dict = takes_a_str("") # pyright: ignore[assignment] takes_a_str(2) # pyright: ignore[arg-type] - 在项目根目录创建
pyrightconfig.json,开启严格模式:{ "strict": true, "reportUnusedVariable": "error" } - 把pyright的运行命令加到pytest的自定义测试函数中,或者直接用脚本执行pyright,检查输出是否只有你标记的错误被忽略——如果出现其他未标记的错误,就说明测试不通过。
3. 补充:专门的类型测试工具
如果需要更精细化的类型测试,还可以尝试typeguard,不过它偏重于运行时类型验证,适合需要在运行时确保类型正确性的场景;要是你要的是静态类型检查的负向测试,前面两种方法会更直接适用。
内容的提问来源于stack exchange,提问作者Arthur Tacca
相关产品推荐
相关产品推荐

