如何使用Django + Nose正确测试覆盖 [英] How to test coverage properly with Django + Nose

查看:155
本文介绍了如何使用Django + Nose正确测试覆盖的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前有一个项目被配置为通过Django的管理命令来运行coverage:

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

这样会产生如下报告:

 名称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
-------- -------------------------------------------------- ----------------
总计107 92 4 0 17%

有一个问题不过是报道。这是不对的。尽管事实上他们确实被测试所覆盖,但覆盖范围是缺少标线。例如,如果我通过 nosetests 而不是django的管理命令运行测试,我得到以下正确的报告:

 名称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
------------------------------------------------ -----------------------------
总计107 25 4 0 77%

Google带领我到覆盖网站的常见问题, http://nedbatchelder.com/code/coverage/faq.html


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



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



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


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

解决方案

在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问题) ,那个woraround是修改你的 manage.py

  import os 
import sys

如果__name__ ==__main__:
#...
from django.core.management import execute_from_command_line

is_testing ='test'in sys.argv

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

execute_from_command_line(sys.argv)

如果is_testing :
cov.stop()
cov.save()
cov.report()

它的工作原理,但它是相当黑客的方法。



更新2



我建议大家使用鼻子来看看py.test( http://pytest.org / ),这是一个非常好的Python测试工具,它与Django很好的集成,还有很多插件等等。我正在使用django-nose,但是尝试了py.test,从未回头。


Currently have a project configured to run coverage via Django's manage command like so:

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

This results in a report like the following:

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%   

There's a problem with this report however. It's wrong. Coverage is marking lines missing, despite the fact that they are indeed being covered by tests. For example, if I run the tests via nosetests instead of django's manage command I get the following correct report:

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 led me to the coverage website's FAQ, http://nedbatchelder.com/code/coverage/faq.html

Q: Why do the bodies of functions (or classes) show as executed, but the def lines do not?

This happens because coverage is started after the functions are defined. The definition lines are executed without coverage measurement, then coverage is started, then the function is called. This means the body is measured, but the definition of the function itself is not.

To fix this, start coverage earlier. If you use the command line to run your program with coverage, then your entire program will be monitored. If you are using the API, you need to call coverage.start() before importing the modules that define your functions.

The question is, can I run the coverage reports properly via Django's manage command? Or do I have to bypass manage to avoid the situation where coverage is started after the "missing" lines are executed?

解决方案

At the moment it's not possible to accurately run coverage alongside with django-nose (because of the way Django 1.7 loads models). So to get the coverage stats, you need to use coverage.py directly from command line, e.g:

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

You can put coverage.py settings into .coveragerc file in the project root (the same dir as manage.py).

This issue is reported on django-nose GitHub page: https://github.com/django-nose/django-nose/issues/180 so maintainers know about the problem, you can let them know that you're also experiencing this issue.

UPDATE

eliangcs pointed out (django-nose issues on GiHub), that woraround is to modify your 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()

It works, but it's rather "hacky" approach.

UPDATE 2

I recommend for everybody that uses nose to have a look at py.test (http://pytest.org/), which is really good Python testing tool, it integrates well with Django, has a lot of plugins and many more. I was using django-nose, but tried py.test and never looked back.

这篇关于如何使用Django + Nose正确测试覆盖的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆