Pytest 和覆盖率:为啥覆盖率结果会随目录结构而变化?

Posted

技术标签:

【中文标题】Pytest 和覆盖率:为啥覆盖率结果会随目录结构而变化?【英文标题】:Pytest and coverage: why do coverage results vary with directory structure?Pytest 和覆盖率:为什么覆盖率结果会随目录结构而变化? 【发布时间】:2016-06-22 13:11:27 【问题描述】:

我有一个在相当大的 django 项目中使用 Pytest 的工作测试套件。问题是我无法使用覆盖率获得正确的结果,我想知道这是否可能是因为项目目录结构。

考虑以下目录树示例:

.
├── apps
│   ├── api
│   │   ├── __init__.py
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   └── views
│   │   │       ├── __init__.py
│   │   │       └── test_tickets.py
│   │   └── views
│   │       ├── __init__.py
│   │       ├── tickets.py
│   ├── support
│   │   ├── __init__.py
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   └── utils
│   │   │       ├── __init__.py
│   │   │       ├── test_management_commands.py
│   │   ├── utils
│   │   │   ├── __init__.py
│   │   │   ├── management_commands.py

以及覆盖率报告的示例输出:

coverage run --source apps/ -m py.test apps/
coverage report

Name                                        Stmts   Miss  Cover
---------------------------------------------------------------
apps/api/views/tickets.py                      42     18    57%
apps/support/utils/management_commands.py     135    100    26%    

查看 html 报告,我可以看到测试执行的许多语句没有被认为是被覆盖的,即使它们应该被覆盖。我认为这个覆盖数据不完整,它似乎只考虑了导入、定义和文档字符串。

无法确定覆盖范围出现错误的原因,我尝试运行单个测试模块,结果是肯定的:

coverage run --source apps/support/utils/management_commands.py -m py.test apps/support/tests/utils/test_management_commands.py
coverage report

Name                                        Stmts   Miss  Cover
---------------------------------------------------------------
apps/support/utils/management_commands.py     135     68    50%

这更准确,HTML 报告显示我已测试的语句被指示为这次涵盖。无法弄清楚为什么运行单个测试模块会产生准确的结果,我通过将测试移动到单个父文件夹下来修改目录结构。

.
├── apps
│   ├── api
│   │   ├── __init__.py
│   │   └── views
│   │       ├── __init__.py
│   │       ├── tickets.py
│   ├── support
│   │   ├── __init__.py
│   │   ├── utils
│   │   │   ├── __init__.py
│   │   │   ├── management_commands.py
├── tests
│   │   ├── __init__.py
│   ├── api
│   │   ├── __init__.py
│   │   └── views
│   │       ├── __init__.py
│   │       └── test_tickets.py
│   ├── support
│   │   ├── __init__.py
│   │   ├── utils
│   │   │   ├── __init__.py
│   │   │   ├── test_management_commands.py

使用此目录结构重新运行覆盖会产生更准确的结果:

coverage run --source apps/ -m py.test tests/
coverage report

Name                                        Stmts   Miss  Cover
---------------------------------------------------------------
apps/api/views/tickets.py                      42      0   100%
apps/support/utils/management_commands.py     135     68    50%

谁能解释为什么在原始目录结构下使用 py.test 运行覆盖会产生完全覆盖?目录结构实际上是问题还是我在这里遗漏了其他东西?

附加信息:

# pytest.ini
[pytest]
addopts = --nomigrations
markers =
    slowtest: mark a test as being slow
    integration: mark a test as being an integration test

INSTALLED_APPS += ('django_coverage', )
TEST_DISCOVER_PATTERN = 'test_*'
COVERAGE_MODULE_EXCLUDES = [
    'settings',
    'urls$',
    'locale$',
    'tests$',
    'django',
    'migrations',
    'compressor',
    'templates?$',
    'fixtures$',
    'static$',
]
ROOT_PATH = os.path.abspath('%s/' % os.path.dirname(__file__))

.coveragerc

[run]
source = apps
omit =
     apps/*/templates?/*
     apps/*/migrations/*
     apps/*/factories/*
     apps/*/tests/*
[html]
directory = coverage

模块版本(有些可能不相关):

pytest==2.9.0
pytest-cov==2.2.1
pytest-django==2.9.1
django-coverage==1.2.4
coverage==4.0.3

【问题讨论】:

看起来您可能启用了 pytest-cov 插件,并且您正在覆盖范围内运行 py.test。您应该选择一种技术。两者一起使用肯定会造成一些混乱。 覆盖相关代码的测试是否真的使用旧的目录结构运行?您可以通过修改它们来轻松检查它们,以便它们应该失败,并查看测试结果中是否弹出断言错误。 感谢您的回复,我已删除 pytest-cov 并验证它在运行 pytest 时未列为插件。但是,旧目录结构的覆盖仍然会产生不完整的结果。我还添加了一些会失败的断言,并且重新运行测试产生的失败表明测试实际上正在运行。我将继续挖掘,看看为什么会这样,任何进一步的建议将不胜感激。 【参考方案1】:

我知道我参加聚会有点晚了(3 岁的问题还没有被接受的答案),但是因为我刚刚遇到了同样的question 和一个看似明显的answer:@987654323 @ 只会报告实际运行的代码。因此,如果您的测试没有调用任何代码,并且在应用程序正常加载期间没有运行,coverage 将不会显示该代码的报告。不运行的代码不会导致错误:)

【讨论】:

以上是关于Pytest 和覆盖率:为啥覆盖率结果会随目录结构而变化?的主要内容,如果未能解决你的问题,请参考以下文章

具有线路覆盖率和最小限制的 Pytest 覆盖率,如业力/伊斯坦布尔

pytest基础

pytest基础

pytest基础

Pytest08-pytest工具与插件

Pytest测试用例介绍