Подтвердить что ты не робот

Как правильно проверить покрытие с помощью Django + Nose

В настоящее время проект настроен для запуска покрытия с помощью команды управления 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, я получаю следующий правильный отчет:

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 не?

Это происходит потому, что покрытие запускается после того, как функции определены. Строки определения выполняются без измерения покрытия, затем начинается освещение, затем вызывается функция. Это означает, что тело измеряется, но определение самой функции не является.

Чтобы исправить это, начните покрытие раньше. Если вы используете командную строку для запуска вашей программы с охватом, ваша программа будет контролироваться. Если вы используете API, перед импортом модулей, которые определяют ваши функции, вы должны вызвать функцию coverage.start().

Вопрос в том, могу ли я правильно запускать отчеты о покрытии с помощью команды управления Django? Или мне нужно обойти управление, чтобы избежать ситуации, когда покрытие запускается после выполнения "отсутствующих" строк?

4b9b3361

Ответ 1

В настоящий момент невозможно точно выполнить покрытие наряду с джанго-носом (из-за того, как модели Django 1.7 загружают модели). Поэтому, чтобы получить статистику покрытия, вам нужно использовать cover.py непосредственно из командной строки, например:

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

Вы можете установить параметры покрытия .py в файл .coveragerc в корне проекта (тот же каталог, что и manage.py).

Эта проблема сообщается на странице django-nose GitHub: https://github.com/django-nose/django-nose/issues/180, чтобы сопровождающие знали о проблеме, вы можете сообщить им, что вы Эта проблема также возникает.

UPDATE

eliangcs указала (проблемы с Django-носом на GiHub), что важно изменить ваш 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()

Это работает, но это скорее "хакерский" подход.

ОБНОВЛЕНИЕ 2

Я рекомендую всем, кто использует нос, чтобы посмотреть на py.test(http://pytest.org/), что является действительно хорошим инструментом тестирования Python, он хорошо интегрируется с Django, имеет множество плагинов и многое другое. Я использовал django-нос, но попробовал py.test и никогда не оглядывался назад.

Ответ 2

Как говорят документы, "используйте командную строку для запуска вашей программы с покрытием":

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

Ответ 3

Мне удалось получить эту работу, включая

import coverage

поверх моего файла manage.py(вместо этого я использую Flask, но имею ту же проблему)

Моя проблема в том, что она работает с консоли, но Дженкинс не знает об этом и продолжает говорить о том, что эти импортные данные выходят за рамки тестов...

Любая идея?

Ответ 4

У меня была такая же проблема с использованием удаленного интерпретатора в виртуальной машине через конфигурацию ssh. Решение состояло в том, чтобы установить каталог моих тестов и ВСЕ его родительские каталоги в "Отображениях пути" раздела "Окружающая среда" в разделе "Выполнить" > "Редактировать конфигурации...".

Ответ 5

Я провел некоторое время с этой проблемой, и даже с учетом ответов, они не были достаточно подробными, чтобы полностью объяснить, что я испытывал. Вот то, что хорошо работает для меня сейчас, согласно ответу от 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 installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc

    # See https://stackoverflow.com/info/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 Я удалил, используя пакет cover и with-coverage cover, поскольку это уже обрабатывается в manage.py сейчас. Вот мои настройки с некоторыми пояснениями:

TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
# These are global options, trim as needed
# See https://stackoverflow.com/info/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 желанию или оставить его полностью, если вы собираетесь использовать флаги каждый раз в командной строке. Ура!