如何使用Django + Nose正确测试覆盖率

时间:2014-07-10 03:50:14

标签: python django code-coverage django-nose

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

./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%   

Google引导我访问了该网站的常见问题解答http://nedbatchelder.com/code/coverage/faq.html

  

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

     

这是因为在定义函数后启动了coverage。定义行在没有覆盖测量的情况下执行,然后启动覆盖,然后调用该函数。这意味着身体被测量,但功能本身的定义不是。

     

要解决此问题,请提前开始报道。如果使用命令行以覆盖范围运行程序,则将监视整个程序。如果您使用的是API,则需要在导入定义函数的模块之前调用coverage.start()。

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

5 个答案:

答案 0 :(得分:13)

目前不可能准确地与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设置放入项目根目录中的.coveragerc文件(与manage.py相同的目录)。

这个问题在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并且从未回头。

答案 1 :(得分:5)

正如文档所说,&#34;使用命令行以覆盖范围运行程序&#34;:

coverage run --branch --source=notify ./manage.py test

答案 2 :(得分: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或将其完全省略。干杯!

答案 3 :(得分:-1)

我设法让这项工作包括

import coverage

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

我的问题是它可以从控制台运行,但Jenkins并不知道它,并继续说这些导入不在测试中......

有什么想法吗?

答案 4 :(得分:-1)

通过ssh配置在虚拟机中使用远程解释器时遇到了同样的问题。解决方案是在&#34; Path映射&#34;中设置我的测试目录和 ALL 其父目录。环境&#34; &#34; Run&#34; &GT; &#34;编辑配置......&#34;。