Test Coverage

You can integrate Coverage.py with behave-django to find out the test coverage of your code.

There are two ways to do this. The simple (and obvious one) is via invocation through the coverage CLI. Alternatively, you can integrate Coverage in the environment.py file of your BDD test setup as shown below.

Prerequisites

Obviously, you need to install Coverage to measure code coverage, e.g.

$ pip install coverage[toml]

Invoke via coverage

Instead of using python manage.py, you simply use coverage run manage.py to invoke your BDD tests, e.g.

$ coverage run manage.py behave

Afterwards, you can display a coverage report in your terminal to understand which lines your tests are missing, e.g.

$ coverage report --show-missing

Tip

A Django project setup with coverage configured in pyproject.toml and executed by Tox is show-cased in the Painless CI/CD Copier template for Django.

Integrate via environment.py

In environment.py, add the code snippet below in the before_all function to start measuring test coverage:

import coverage

def before_all(context):
    cov = coverage.Coverage()
    cov.start()
    context.cov = cov

Then write below code to end up measuring test coverage. You can save the coverage result on html format.

def after_all(context):
    cov = context.cov
    cov.stop()
    cov.save()
    cov.html_report(directory="./cov")

You can check the test coverage on the web with the following command.

$ python -m http.server --directory ./cov

Warning

Internally, the time before_all is executed seems to be later than the time when django loads the modules set in each app.

So sometimes it is necessary to reload django app’s modules for accurate test coverage measurement.

Like this:

import inspect
import importlib

def reload_modules():
import your_app1
import your_app2

for app in [your_app1, your_app2]:
    members = inspect.getmembers(app)
    modules = map(
        lambda keyval: keyval[1],
        filter(lambda keyval: inspect.ismodule(keyval[1]), members),
    )
    for module in modules:
        try:
            importlib.reload(module)
        except:
            continue
def before_all(context):
# cov
cov = coverage.Coverage()
cov.start()
context.cov = cov

# modules
reload_modules()