16

目前有一个项目配置为通过 Django 的管理命令运行覆盖,如下所示:

./manage.py test --with-coverage --cover-package=notify --cover-branches --cover-inclusive --cover-erase

这会生成如下报告:

Name                        Stmts   Miss Branch BrMiss  Cover   Missing
--------------------------------------------------------------------------
notify.decorators               4      1      0      0    75%   4
notify.handlers                 6      1      2      0    88%   11
notify.notification_types      46     39      2      0    19%   8-55, 59, 62, 66
notify.notifications           51     51      0      0     0%   11-141
--------------------------------------------------------------------------
TOTAL                         107     92      4      0    17%   

然而,这份报告有一个问题。这是错的。覆盖是标记线缺失,尽管它们确实被测试覆盖。例如,如果我通过nosetests而不是 django 的 manage 命令运行测试,我会得到以下正确的报告:

Name                        Stmts   Miss Branch BrMiss  Cover   Missing
-----------------------------------------------------------------------------
notify.decorators               4      0      0      0   100%   
notify.handlers                 6      0      2      0   100%   
notify.notification_types      46      0      2      0   100%   
notify.notifications           51     25      0      0    51%   13, 18, 23, 28, 33, 38, 43, 48, 53, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 116, 121, 126, 131, 136, 141
-----------------------------------------------------------------------------
TOTAL                         107     25      4      0    77%   

谷歌把我带到了覆盖网站的常见问题解答,http://nedbatchelder.com/code/coverage/faq.html

问:为什么函数(或类)的主体显示为已执行,但 def 行没有?

发生这种情况是因为在定义函数之后才开始覆盖。定义行在没有覆盖测量的情况下执行,然后开始覆盖,然后调用函数。这意味着身体被测量,但功能本身的定义不是。

要解决此问题,请尽早开始报道。如果你使用命令行来运行你的程序,那么你的整个程序都会被监控。如果您使用 API,则需要在导入定义您的函数的模块之前调用 coverage.start()。

问题是,我可以通过 Django 的管理命令正确运行覆盖率报告吗?或者我是否必须绕过管理以避免在执行“缺失”行后开始覆盖的情况?

4

5 回答 5

15

目前不可能与 django-nose 一起准确地运行覆盖(因为 Django 1.7 加载模型的方式)。因此,要获取覆盖率统计信息,您需要直接从命令行使用 coverage.py,例如:

$ coverage run --branch --source=app1,app2 ./manage.py test
$ coverage report
$ coverage html -d coverage-report

您可以将coverage.py 设置放入项目根目录(与manage.py 相同的目录)中的.coveragerc 文件中。

这个问题在 django-nose GitHub 页面上报告:https ://github.com/django-nose/django-nose/issues/180所以维护者知道这个问题,你可以让他们知道你也遇到了这个问题.

更新

eliangcs 指出(GiHub 上的 django-nose 问题),解决方法是修改您的manage.py

import os
import sys

if __name__ == "__main__":
    # ...
    from django.core.management import execute_from_command_line

    is_testing = 'test' in sys.argv

    if is_testing:
        import coverage
        cov = coverage.coverage(source=['package1', 'package2'], omit=['*/tests/*'])
        cov.erase()
        cov.start()

    execute_from_command_line(sys.argv)

    if is_testing:
        cov.stop()
        cov.save()
        cov.report()

它有效,但它是相当“hacky”的方法。

更新 2

推荐所有使用nose的人看看py.test(http://pytest.org/),这是一个非常好的Python测试工具,它与Django集成得很好,有很多插件等等。我正在使用 django-nose,但尝试了 py.test 并且从未回头。

于 2015-04-15T12:29:51.600 回答
6

正如文档所说,“使用命令行运行覆盖范围的程序”:

coverage run --branch --source=notify ./manage.py test
于 2014-07-10T10:25:55.617 回答
0

我花了一些时间来解决这个问题,即使给出了答案,它们也不够详细,无法完全解释我的经历。根据iyn的回答,这对我现在很有效,并进行了一些必要的调整。我的 manage.py 看起来像这样:

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc

    # See https://stackoverflow.com/questions/24668174/how-to-test-coverage-properly-with-django-nose
    is_coverage_testing = 'test' in sys.argv and '--with-coverage' in sys.argv
    # Drop dupe with coverage arg
    if '--with-coverage' in sys.argv:
        sys.argv.remove('--with-coverage')

    if is_coverage_testing:
        import coverage
        cov = coverage.coverage(source=['client_app', 'config_app', 'list_app', 'core_app', 'feed_app',
                                        'content_app', 'lib',
                                        'job_app', 'license_app', 'search_app', 'weather_app'],
                                omit=['*/integration_tests/*'])
        cov.erase()
        cov.start()

    execute_from_command_line(sys.argv)

    if is_coverage_testing:
        cov.stop()
        cov.save()
        cov.report()

如上所示,我包含了所有用于测试的应用程序,并排除了我保存集成测试的地方。

我的settings.py我放弃了使用封面包,with-coverage因为现在已经处理了manage.py。这是我的设置和一些解释:

TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
# These are global options, trim as needed
# See https://stackoverflow.com/questions/24668174/how-to-test-coverage-properly-with-django-nose
NOSE_ARGS = [
    # '--cover-package=client_app',  # included in manage.py (hack to include all app testing)
    # '--cover-package=config_app',
    # '--cover-package=content_app',
    # '--cover-package=job_app',
    # '--cover-package=lib',
    # '--cover-package=license_app',
    # '--cover-package=list_app',
    # '--cover-package=search_app',
    # '--cover-package=core_app',
    # '--cover-package=weather_app',
    # '--cover-package=feed_app',
    '--logging-level=INFO',
    '--cover-erase',
    # '--with-coverage',  # Included in manage.py (hack), do not use here or will create multiple reports
    # '--cover-branches',  # Lowers coverage
    '--cover-html',  # generate HTML coverage report
    '--cover-min-percentage=59',
    # '--cover-inclusive',  # can't get coverage results on most files without this... This breaks django tests.
]

我像这样运行我的基本测试(覆盖范围):

./manage.py test --noinput --verbose --with-coverage

现在我可以看到models.py、admins.py 以及apps.py 被覆盖了。

我像这样运行我的集成测试(没有覆盖):

./manage.py test integration_tests/itest_*  --noinput

我还可以像这样运行一组特定的测试:

./manage.py test --noinput --verbose client_app/tests.py

NOSE_ARGS如果您打算每次都在命令行上使用这些标志,您也可以根据需要进行修改或完全忽略它。干杯!

于 2019-02-06T20:50:00.463 回答
-1

我已经设法让这个工作包括

import coverage

在我的 manage.py 文件之上(我使用的是 Flask 但有同样的问题)

我的问题是它可以从控制台工作,但詹金斯并没有意识到这一点,并一直说这些导入不在测试范围内......

任何的想法?

于 2014-10-24T12:51:58.903 回答
-1

我通过 ssh 配置在虚拟机中使用远程解释器时遇到了同样的问题。解决方案是在“运行”>“编辑配置...”的“环境”部分的“路径映射”中设置我的测试目录及其所有父目录。

于 2015-01-28T21:28:49.077 回答