'No data to report' when i execute coverage report

Viewed 860

I use django.test to do unittest

At first i run

coverage run ./manage.py test audit.lib.tests.test_prune

And works well

----------------------------------------------------------------------
Ran 1 test in 1.493s

OK

But when i run coverage report, unexpected happens, it should show some reports as expected, but No data to report

root@0553f9cad609:/opt/buildaudit# coverage report
No data to report.

I have no ideas, it has confused me whole day.. Thank you all!

1 Answers

Some of my tests executed helper programs, and I wanted to gather coverage results for those programs too. That meant coverage had to gather and store metrics in multiple processes at the same time. Normally, it stores them in a single file named .coverage, which doesn't work when gathering metrics in parallel. Instead, coverage needs to be told to store results in separate files, one per process, giving them unique file names. Per the docs, that can be done by adding this to .coveragerc.

[run]
parallel = True

The report generators, like coverage html, expect those results to be combined into a single file. That can be done by running this after the tests have finished, and before trying to create a report from them.

% coverage combine

Not doing so produces the No data to report error in the question. Credit goes to @PengQunZhong for first suggesting this.


Going beyond the question a bit, this actually wasn't enough for me to get measurements from all sub-processes. The docs have a good description of the subtleties and solutions, but I'll summarize what I chose. I use the multiprocessing module to start some of the sub-processes, so I had to add the following in the [run] section of .coveragerc.

concurrency = multiprocessing

Also, sub-processes needed to tell coverage to gather metrics since, unlike top-level tests, sub-processes are not run by coverage. I did this by adding the following at the top of the code for each sub-process. See the reference for other options.

    import os
    if "COVERAGE_PROCESS_START" in os.environ:
        import coverage
        coverage.process_startup()

The environment variable used here is recognized by coverage; don't rename it. Also, I ran my tests with the following. I use pytest, but other test frameworks would be done similarly. There's also a pytest plug-in that can help.

% COVERAGE_PROCESS_START=.coveragerc coverage run pytest

Finally, some tests and their sub-processes needed small changes to ensure coverage was allowed to save its results when the process was terminated. An ungraceful exit, SIGKILL, etc. prevent this. coverage writes its results in an atexit hook, and if you have coverage 6.3 or newer, also in a signal handler for SIGTERM. If your sub-processes are terminated any other way, coverage will not be able to save its results. In my case, I usually sent a SIGTERM to the sub-process from its parent. A parent that used subprocess.Popen objects, for example, did this.

kid.terminate()
Related