如何用pytest检查每个函数是否有对应单元测试并区分调用来源?
我需要确保代码库中每个实现的函数都有对应的单元测试。举个例子:
def top_level_function(): sub_function() def sub_function(): print('hello')
如果测试top_level_function时忘了patch/mock子函数调用,原sub_function会被执行——哪怕它没有单元测试,测试覆盖率仍会显示100%。这说明测试覆盖率根本不足以判断单元测试是否充足。
我想搞清楚两个问题:
a) 怎么确认函数是被对应的单元测试调用的(符合预期的测试路径);
b) 怎么确认函数是被父函数调用的(这是测试时要避免的情况)。
有没有额外的pytest包或pytest-cov设置能报告缺失的单元测试?我还没找到合适的工具。
(注:我希望top_level_function的单元测试只在自身出错时失败,而不是因为sub_function有bug才失败,所以测试函数时通常会mock子函数调用。)
补充说明
- 试过pytest-func-cov插件,但它似乎没法正常工作(有公开问题),有没有能用的替代插件?
- PyCharm的Pycrunch插件能显示代码行被哪个单元测试覆盖,但解决不了我的问题,只能辅助识别覆盖异常的代码行。
针对问题a和b:追踪函数的测试调用来源
1. 用pytest-cov的函数级覆盖+自定义标记
pytest-cov本身不直接追踪调用来源,但可以结合自定义测试标记和coverage的context功能,区分不同测试对函数的覆盖:
- 给每个函数的专属测试用例加标记,比如
@pytest.mark.test_sub_function - 在测试
sub_function的用例里,设置coverage的context:
import coverage cov = coverage.Coverage() cov.start() cov.set_context("test_sub_function") # 执行测试逻辑 cov.stop() cov.save()
生成报告时按context筛选,就能看到sub_function的覆盖是来自它自己的测试用例,还是被top_level_function的测试间接触发的。
2. 用pytest-trace插件或自定义追踪装饰器
- pytest-trace:这个插件能追踪每个函数被哪些测试用例调用,运行测试时加
--trace参数,输出里就能看到函数的调用链路,直接区分是被自身测试调用还是父函数调用。 - 自定义装饰器:给需要追踪的函数加个装饰器,记录调用它的测试用例名称:
import inspect import pytest def track_test_calls(func): def wrapper(*args, **kwargs): # 获取当前运行的测试用例名称 frame = inspect.currentframe() while frame: if "request" in frame.f_locals: test_name = frame.f_locals["request"].nodeid print(f"{func.__name__} called by test: {test_name}") break frame = frame.f_back return func(*args, **kwargs) return wrapper # 给函数加装饰器 @track_test_calls def sub_function(): print('hello')
运行测试时就能看到sub_function是被哪个测试触发的,从而判断是否符合预期。
检测缺失单元测试的工具
1. 自定义脚本对比函数与测试用例
用inspect模块遍历代码中的所有函数,再用pytest的API收集所有测试用例,提取每个测试用例直接调用的函数,对比两者就能找出没有对应测试用例的函数。也可以结合pytest-mock-generator生成mock,辅助确认哪些函数没被直接测试。
2. pytest-cov的函数级覆盖强制检查
开启coverage的函数级覆盖,设置--cov-report term --cov-fail-under 100,同时在.coveragerc里配置:
[report] show_missing = True function_coverage = True
这样如果某个函数没有被任何测试直接调用(哪怕被间接覆盖),函数级覆盖率会显示缺失,从而触发失败。不过这个方法需要确保所有间接调用都被mock,否则会误判,得配合mock策略使用。
3. pytest-testmon
这个插件能追踪测试用例和代码的依赖关系,识别哪些代码没有对应的测试用例,还能只运行受代码变更影响的测试。虽然不是直接报告缺失测试,但能帮你发现哪些函数从未被测试用例直接触发。
替代pytest-func-cov的插件
可以试试pytest-cov的函数级覆盖(上面提到的配置),或者pytest-coverage-report插件——它提供更详细的函数级覆盖报告,能明确显示每个函数的覆盖来源。
内容的提问来源于stack exchange,提问作者Stefan

