Code Coverage for Django 1.1
Last week I wanted to implement code coverage for my Django application. So, I did what I normal do; a Google search. I found an old articles and alternate method. I worked with some old code that seemed to work for Django <1.0. I modified it so it now works with Django 1.1. I hope this will be useful to someone else.
With the current configuration the test runner will output the code coverage percentages and generate HTML reports displaying the code covered and code not covered.Output to the console will look similar to this:Ran 95 tests in 113.249s FAILED (errors=19) Name Stmts Exec Cover ------------------------------------------ myApp.datamethods 479 243 50% myApp.methods 150 97 64% myApp.models 432 335 77% myApp.signals 17 17 100% myApp.urls 4 4 100% myApp.views 938 311 33% ------------------------------------------ TOTAL 2020 1007 49% Destroying test database...You will need coverage.py and coverage_color.py for this to work. These should be placed somewhere on your Python path. I included the files below just in case. I also included the test runner code: tests_with_coverage.py, which should be placed in your django project directory. In order for this test runner to function you will also need to modify your settings.py to include two constants:
- TEST_RUNNER points to the location of the tests_with_coverage.py test runner.
- COVERAGE_MODULES is a list of the modules used in the coverage analysis.
settings.py
TEST_RUNNER = 'myApp.tests_with_coverage.run_tests' COVERAGE_MODULES = ['myApp.models', 'myApp.views', 'myApp.datamethods', 'myApp.methods', 'myApp.signals', 'myApp.urls']Run your tests as normal using:./manage.py test myApp The code below was based on the code found here: http://blogs.23.nu/c0re/2007/07/antville-15428/
tests_with_coverage.py
import unittest, os, coverage, coverage_color from django.conf import settings from django.db.models import get_app, get_apps from django.test.testcases import OutputChecker, DocTestRunner, TestCase from django.test.utils import setup_test_environment, teardown_test_environment from django.test.simple import build_test, reorder_suite, build_suite def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]): coveragemodules = [] if hasattr(settings, 'COVERAGE_MODULES'): coveragemodules = settings.COVERAGE_MODULES if coveragemodules: coverage.start() setup_test_environment() settings.DEBUG = False suite = unittest.TestSuite() if test_labels: for label in test_labels: if '.' in label: suite.addTest(build_test(label)) else: app = get_app(label) suite.addTest(build_suite(app)) else: for app in get_apps(): suite.addTest(build_suite(app)) for test in extra_tests: suite.addTest(test) suite = reorder_suite(suite, (TestCase,)) from django.db import connection old_name = settings.DATABASE_NAME connection.creation.create_test_db(verbosity) result = unittest.TextTestRunner(verbosity=verbosity).run(suite) if coveragemodules: coverage.stop() coveragedir = './build/coverage' if hasattr(settings, 'COVERAGE_DIR'): coveragedir = settings.COVERAGE_DIR if not os.path.exists(coveragedir): os.makedirs(coveragedir) modules = [] for module_string in coveragemodules: module = __import__(module_string, globals(), locals(), [""]) modules.append(module) f,s,m,mf = coverage.analysis(module) fp = file(os.path.join(coveragedir, module_string + ".html"), "wb") coverage_color.colorize_file(f, outstream=fp, not_covered=mf) fp.close() coverage.report(modules, show_missing=0) coverage.erase() connection.creation.destroy_test_db(old_name, verbosity) teardown_test_environment() return len(result.failures) + len(result.errors)
