Login

Test runner with coverage

Author:
nicklane
Posted:
April 15, 2008
Language:
Python
Version:
.96
Score:
2 (after 2 ratings)

A replacement test runner which outputs a coverage report after the tests. Simply change your TEST_RUNNER setting to point to run_tests_with_coverage and you're good to go.

Note that 'as-is' this snippet reports the coverage of all modules underneath the app, by walking the directory tree and loading all of the .py modules (this may be a naive approach).

If you change it to use get_coverage_modules() instead, it will only display the coverage of modules that have been imported by the test suite, using the Python inspect lib, which may be more reliable.

Uses coverage.py. Based on ideas from: 1, 2 and 3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import coverage
import os
from inspect import getmembers, ismodule

from django.test.simple import run_tests as base_run_tests
from django.db.models import get_app, get_apps


def get_coverage_modules(app_module):
    """
    Returns a list of modules to report coverage info for, given an
    application module.
    """
    app_path = app_module.__name__.split('.')[:-1]
    coverage_module = __import__('.'.join(app_path), {}, {}, app_path[-1])

    return [coverage_module] + [attr for name, attr in
        getmembers(coverage_module) if ismodule(attr) and name != 'tests']

def get_all_coverage_modules(app_module):
    """
    Returns all possible modules to report coverage on, even if they
    aren't loaded.
    """
    # We start off with the imported models.py, so we need to import
    # the parent app package to find the path.
    app_path = app_module.__name__.split('.')[:-1]
    app_package = __import__('.'.join(app_path), {}, {}, app_path[-1])
    app_dirpath = app_package.__path__[-1]

    mod_list = []
    for root, dirs, files in os.walk(app_dirpath):
        root_path = app_path + root[len(app_dirpath):].split(os.path.sep)[1:]
        for file in files:
            if file.lower().endswith('.py'):
                mod_name = file[:-3].lower()
                try:
                    mod = __import__('.'.join(root_path + [mod_name]), {}, {},
                        mod_name)
                except ImportError:
                    pass
                else:
                    mod_list.append(mod)

    return mod_list

def run_tests_with_coverage(test_labels, verbosity=1, interactive=True,
        extra_tests=[]):
    """
    Test runner which displays a code coverage report at the end of the
    run.
    """
    coverage.use_cache(0)
    coverage.start()
    results = base_run_tests(test_labels, verbosity, interactive, extra_tests)
    coverage.stop()

    coverage_modules = []
    if test_labels:
        for label in test_labels:
            # Don't report coverage if you're only running a single
            # test case.
            if '.' not in label:
                app = get_app(label)
                coverage_modules.extend(get_all_coverage_modules(app))
    else:
        for app in get_apps():
            coverage_modules.extend(get_all_coverage_modules(app))

    if coverage_modules:
        coverage.report(coverage_modules, show_missing=1)

    return results

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

Please login first before commenting.