目前有一个项目配置为通过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命令正确运行覆盖率报告吗?或者我是否必须绕过管理以避免在执行“缺失”行之后启动覆盖率的情况?
答案 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;。