我对项目中不同文件的覆盖行为差异感到困惑,希望有人建议如何比我现在更有效地调试它。这基本上与 Pytest 错误地报告覆盖范围中缺失的行
是同一个问题我正在使用Python 3.11,tox 4.11.3,覆盖率7.4.4。如果有的话,可以在mbp笔记本电脑上工作。我的python代码文件都是这样的,我的包目录
myproj/
中的这个叫coverme.py
:
import logging
logger = logging.getLogger(__name__)
some_obj = SomeObject(foo='bar')
CONST = 123
def myfunc() -> bool:
return True
def otherfunc() -> bool:
"""
nice pydoc goes here
"""
return CONST
我的测试文件都是这样的,项目的
tests/
目录下的这个叫test_coverme.py
:
import logging
from myproj import coverme
logger = logging.getLogger(__name__)
def test_myfunc():
logger.debug('test myfunc')
assert coverme.myfunc()
这是我的 tox.ini 文件:
[tox]
envlist = code,flake8
minversion = 2.0
[pytest]
testpaths = tests
[testenv:code]
basepython = python3
deps=
coverage
pytest
pytest-cov
pytest-mock
commands =
pytest --cov myproj --cov-report term-missing --cov-fail-under=70 {posargs}
[testenv:flake8]
basepython = python3
skip_install = true
deps = flake8
commands = flake8 setup.py myproj tests
我可以通过
tox -- tests/test_coverme.py
运行测试并查看这些结果;我删除了无法在此处发布的文件的结果:
---------- coverage: platform darwin, python 3.11.7-final-0 ----------
Name Stmts Miss Cover Missing
-------------------------------------------------------------
myproj/coverme.py 7 1 86% 16
上面显示的简单
coverme
示例完美运行,pytest-cov 报告除第 16 行之外的所有行;我同意 otherfunc
永远不会被调用。我的项目中的大多数代码文件也是如此,报告的覆盖率符合我的预期。
然而,对于一些代码文件(我当然不能在这里发布),覆盖率报告列出了未覆盖(遗漏)的行,即导入语句和常量定义!例如,像
CONST = 123
这样的 pytest 声明行(出现在我上面发布的示例中)不会被任何测试覆盖。测试导入的文件中的常量赋值行似乎不可能不被执行。
我在报道常见问题解答中看到从命令行调用
coverage
的建议,我相信 tox 正在为我做这件事。我运行 coverage erase
来清理旧数据,这没有什么区别。我运行 tox -r
重新安装所有依赖项,也没有区别。我知道我可以使用 tox 运行单个测试文件或单个测试(不是完整套件),但我也没有发现这会产生影响。
请建议其他方法来找出我做错了什么,或者只是 pytest 做得不太正确,提前致谢。
缺乏类似的问题/答案让我非常确定我做错了什么,或者至少有点不寻常。我注意到第一行missed始终是此行之后的行:
api_client = ApiClient(client_config)
Pdb 中的大量跟踪确认这是有问题的代码行。 该类
ApiClient
是由 swagger-codegen
从 OpenAPI 规范 YAML 文件生成的。 ApiClient
类在其__init__
方法中创建一个常规的Python3线程池,如下所示:
self.pool = ThreadPool()
库
Pool
类的 __init__
方法调用 _repopulate_pool
方法,该方法创建进程并在进程上调用 .start()
。此时,Python 调试器将停止跟踪并继续执行。我怀疑(但不知道如何证明)当 coverage
工具监视执行时会发生类似的情况,这就是它无法跟踪执行行的原因。
我的解决方案是一种解决方法。我没有使用模块顶部的 API 客户端初始化变量,而是添加了一个 getter 函数来延迟创建
ApiClient
的实例:
_api_client = None
def _get_api_client() -> ApiClient:
"""
Initialize an API object lazily to simplify tox testing.
:return: ApiClient
"""
global _api_client
if _api_client is None:
client_config = Configuration()
_api_client = ApiClient(client_config)
return _api_client
对于毒性测试,我使用
mocker.patch
从这个 getter 函数返回一个假值,假的 bcos 我的测试套件不会模拟这些生成的客户端访问的远程 REST 服务。
完成所有这些后,我再次看到符合我预期的覆盖率结果,并且模块顶部的所有导入和赋值语句不再被错过。
也许这会对使用 Swagger 生成的代码的人有所帮助。