How to test coverage properly with Django + Nose
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 osimport sysif __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.
As the docs say, "use the command line to run your program with coverage":
coverage run --branch --source=notify ./manage.py test
I spent sometime with this problem, and even with the answers given, they were not detailed enough to fully explain what I was experiencing. Here is what works well for me now, as per answer from iyn with a few necessary tweaks. My manage.py looks like this:
#!/usr/bin/env pythonimport osimport sysif __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()
As can be seen above, I included all my apps for testing and excluded where I keep my integration tests.
My settings.py
I dropped using cover package and with-coverage
as this is already handled in manage.py
now. Here is my settings with some explanations:
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-noseNOSE_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.]
I run my basic tests like so (with coverage):
./manage.py test --noinput --verbose --with-coverage
And now I can see models.py, admins.py as well as apps.py gets covered.
I run my integration tests like so (without coverage):
./manage.py test integration_tests/itest_* --noinput
I can also run a specific set of tests like so:
./manage.py test --noinput --verbose client_app/tests.py
You can also modify NOSE_ARGS
as you wish or leave it out completely if you intend to use the flags each time on the command line. Cheers!