从 pip 转移到 poetry,现在 pytest-cov 不会收集覆盖率数据
Moved from pip to poetry and now pytest-cov won't collect coverage data
我最近开始使用 poetry
来管理项目依赖关系,
而不是使用 requirements.txt
和 test-requirements.txt
和
pip
.
进行更改后,我无法进行覆盖测试
正确。在这两种情况下,我都使用 tox
来驱动测试(而且我
安装了 tox-poetry
扩展。
我的 tox.ini
目前是这样的:
[tox]
isolated_build = True
envlist = pep8,unit
[testenv]
whitelist_externals = poetry
[testenv:venv]
commands = {posargs}
[testenv:pep8]
commands =
poetry run flake8 {posargs:symtool}
[testenv:unit]
commands =
poetry run pytest --cov=symtool {posargs} tests/unit
以前是这样的:
[tox]
envlist = pep8,unit
[testenv]
usedevelop = True
install_command = pip install -U {opts} {packages}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:venv]
commands = {posargs}
[testenv:pep8]
commands =
flake8 {posargs:symtool}
[testenv:unit]
commands =
pytest --cov=symtool {posargs} tests/unit
自从更改为 poetry
后,当我 运行 例如tox -e unit
,我看到了:
unit run-test: commands[0] | poetry run pytest --cov=symtool tests/unit
===================================== test session starts =====================================
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
cachedir: .tox/unit/.pytest_cache
rootdir: /home/lars/projects/symtool, configfile: tox.ini
plugins: cov-2.11.1
collected 14 items
tests/unit/test_disasm.py ..... [ 35%]
tests/unit/test_symtool.py ......... [100%]r
Coverage.py warning: No data was collected. (no-data-collected)
我正在尝试解决 no-data-collected
问题。根据
pytest --help
,--cov
参数设置路径或包
姓名:
--cov=[SOURCE] Path or package name to measure during execution
存储库的根目录(在上面的输出中是 rootdir
来自 tox
) 看起来像这样:
asm
pyproject.toml
README.md
reference
symtool
tests
tox.ini
肯定有一个 symtool
目录包含这个包。
即使测试以某种方式不在项目根目录中 运行ning
目录, symtool
包安装在测试环境中,
单元测试实际上通过了这一事实(所有
其中包括 import symtool
).
的一些变体
如何让保险恢复正常?
如果您使用诗歌,您可能也喜欢在 tox 中使用 pytest、flake8 等的锁定依赖项。这可以通过 tox-poetry-installer.
来实现
为了确保您的测试 运行 是针对构建和安装的包而不是本地文件,您必须为 pytest 使用 --import-mode 标志并将其设置为 importlib
。
要使用 pytest-cov 测量覆盖率,您必须指向 {envsitepackagesdir}。
总而言之,您的 tox.ini
可能如下所示:
[tox]
isolated_build = true
requires =
tox-poetry-installer[poetry] == 0.6.0
envlist = py39
[testenv]
locked_deps =
pytest
pytest-cov
commands = pytest --cov {envsitepackagesdir}/mypackage --import-mode=importlib
--import-mode
在做什么?
为了运行测试,pytest
需要导入测试模块。传统上,这是通过将发现的测试文件夹所在的根文件夹的路径添加到 sys.path 来完成的。通常测试文件夹和包文件夹共享同一个根文件夹。所以添加到 sys.path 的副作用是,测试 运行 针对包文件夹而不是安装的包,因为 python 首先找到这个文件夹。
可以建议 pytest
将发现的根文件夹附加到它,而不是添加到 sys.path
之前。这样做 python 将在尝试导入时首先查看 site-packages 文件夹。因此可以针对已安装的软件包进行测试。
操纵 sys.path
几乎总是一个坏主意,因为它会导致不必要的副作用。 pytest
的docs描述一个:
Same as prepend, requires test module names to be unique when the test directory tree is not arranged in packages, because the modules will put in sys.modules after importing.
importlib
的第三个选项是在 pytest 6 中引入的。它使用 python 的内置 importlib
来动态加载测试模块而无需操作 sys.path
.他们计划在未来的版本中将此选项设为默认选项。
将我的评论变成答案:
这是 pytest-cov
和 tox
的老问题(首次报告于 issue #38)。 tox
将您的项目安装为第三方包,pytest
将从站点导入它(例如,如果作业名为 unit
,则从 .tox/unit/lib/python3.X/site-packages
导入),而 --cov=symtool
指示 pytest-cov
收集项目根目录中 symtool
目录的覆盖率。
一种解决方案是切换到 src
布局:
├── pyproject.toml
├── README.md
├── reference
├── src
| └── symtool
├── tests
└── tox.ini
在您的 pyproject.toml
中,您需要将 poetry
指向源目录:
[tool.poetry]
...
packages = [
{ include = "symtool", from = "src" },
]
现在 src
将阻止从源代码树导入代码,因此 --cov=symtool
将收集已安装模块的覆盖率,这是唯一的选择。对于开发过程的其余部分,您不应该遇到麻烦,因为 poetry install
以可编辑模式安装项目,因此其余部分应该可以正常工作。
另一种选择是使用 tox
跳过包安装,而是以可编辑模式安装。放置在 tox.ini
中的示例片段:
[testenv]
whitelist_externals =
poetry
skip_install = true
commands_pre =
poetry install
commands =
poetry run pytest ...
虽然这几乎杀死了对已安装模块的测试(您停止明确测试您的项目是否可以安装在空白 venv 中,而是使用源代码树中的内容),所以我会选择 src
布局选项。
我最近开始使用 poetry
来管理项目依赖关系,
而不是使用 requirements.txt
和 test-requirements.txt
和
pip
.
进行更改后,我无法进行覆盖测试
正确。在这两种情况下,我都使用 tox
来驱动测试(而且我
安装了 tox-poetry
扩展。
我的 tox.ini
目前是这样的:
[tox]
isolated_build = True
envlist = pep8,unit
[testenv]
whitelist_externals = poetry
[testenv:venv]
commands = {posargs}
[testenv:pep8]
commands =
poetry run flake8 {posargs:symtool}
[testenv:unit]
commands =
poetry run pytest --cov=symtool {posargs} tests/unit
以前是这样的:
[tox]
envlist = pep8,unit
[testenv]
usedevelop = True
install_command = pip install -U {opts} {packages}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:venv]
commands = {posargs}
[testenv:pep8]
commands =
flake8 {posargs:symtool}
[testenv:unit]
commands =
pytest --cov=symtool {posargs} tests/unit
自从更改为 poetry
后,当我 运行 例如tox -e unit
,我看到了:
unit run-test: commands[0] | poetry run pytest --cov=symtool tests/unit
===================================== test session starts =====================================
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
cachedir: .tox/unit/.pytest_cache
rootdir: /home/lars/projects/symtool, configfile: tox.ini
plugins: cov-2.11.1
collected 14 items
tests/unit/test_disasm.py ..... [ 35%]
tests/unit/test_symtool.py ......... [100%]r
Coverage.py warning: No data was collected. (no-data-collected)
我正在尝试解决 no-data-collected
问题。根据
pytest --help
,--cov
参数设置路径或包
姓名:
--cov=[SOURCE] Path or package name to measure during execution
存储库的根目录(在上面的输出中是 rootdir
来自 tox
) 看起来像这样:
asm
pyproject.toml
README.md
reference
symtool
tests
tox.ini
肯定有一个 symtool
目录包含这个包。
即使测试以某种方式不在项目根目录中 运行ning
目录, symtool
包安装在测试环境中,
单元测试实际上通过了这一事实(所有
其中包括 import symtool
).
如何让保险恢复正常?
如果您使用诗歌,您可能也喜欢在 tox 中使用 pytest、flake8 等的锁定依赖项。这可以通过 tox-poetry-installer.
来实现为了确保您的测试 运行 是针对构建和安装的包而不是本地文件,您必须为 pytest 使用 --import-mode 标志并将其设置为 importlib
。
要使用 pytest-cov 测量覆盖率,您必须指向 {envsitepackagesdir}。
总而言之,您的 tox.ini
可能如下所示:
[tox]
isolated_build = true
requires =
tox-poetry-installer[poetry] == 0.6.0
envlist = py39
[testenv]
locked_deps =
pytest
pytest-cov
commands = pytest --cov {envsitepackagesdir}/mypackage --import-mode=importlib
--import-mode
在做什么?
为了运行测试,pytest
需要导入测试模块。传统上,这是通过将发现的测试文件夹所在的根文件夹的路径添加到 sys.path 来完成的。通常测试文件夹和包文件夹共享同一个根文件夹。所以添加到 sys.path 的副作用是,测试 运行 针对包文件夹而不是安装的包,因为 python 首先找到这个文件夹。
可以建议 pytest
将发现的根文件夹附加到它,而不是添加到 sys.path
之前。这样做 python 将在尝试导入时首先查看 site-packages 文件夹。因此可以针对已安装的软件包进行测试。
操纵 sys.path
几乎总是一个坏主意,因为它会导致不必要的副作用。 pytest
的docs描述一个:
Same as prepend, requires test module names to be unique when the test directory tree is not arranged in packages, because the modules will put in sys.modules after importing.
importlib
的第三个选项是在 pytest 6 中引入的。它使用 python 的内置 importlib
来动态加载测试模块而无需操作 sys.path
.他们计划在未来的版本中将此选项设为默认选项。
将我的评论变成答案:
这是 pytest-cov
和 tox
的老问题(首次报告于 issue #38)。 tox
将您的项目安装为第三方包,pytest
将从站点导入它(例如,如果作业名为 unit
,则从 .tox/unit/lib/python3.X/site-packages
导入),而 --cov=symtool
指示 pytest-cov
收集项目根目录中 symtool
目录的覆盖率。
一种解决方案是切换到 src
布局:
├── pyproject.toml
├── README.md
├── reference
├── src
| └── symtool
├── tests
└── tox.ini
在您的 pyproject.toml
中,您需要将 poetry
指向源目录:
[tool.poetry]
...
packages = [
{ include = "symtool", from = "src" },
]
现在 src
将阻止从源代码树导入代码,因此 --cov=symtool
将收集已安装模块的覆盖率,这是唯一的选择。对于开发过程的其余部分,您不应该遇到麻烦,因为 poetry install
以可编辑模式安装项目,因此其余部分应该可以正常工作。
另一种选择是使用 tox
跳过包安装,而是以可编辑模式安装。放置在 tox.ini
中的示例片段:
[testenv]
whitelist_externals =
poetry
skip_install = true
commands_pre =
poetry install
commands =
poetry run pytest ...
虽然这几乎杀死了对已安装模块的测试(您停止明确测试您的项目是否可以安装在空白 venv 中,而是使用源代码树中的内容),所以我会选择 src
布局选项。