python-3.xnose2

Unable to get missing lines & branches in nose2


I'm trying to get my tests to report missing lines and branches. I've configured nose2 and coverage to produce line and branch coverage, and as far as I can tell I have set the coverage config correctly to give me the missing lines and branches. However I can't get coverage to give me missing column when run under nose2, but I can if I run it directly.


Consider the following setup in my project directory.

my_module/
    __init__.py
    main.py
tests/
    test_a_thing.py
unittest.cfg
.coveragerc

contents of .coveragerc

[run]
branch = True

[report]
show_missing = True

contents of unittest.cfg

[coverage]
always-on = True
coverage = my_module
coverage-config = .coveragerc

the output of my nose2 command

(example_venv) andy@batman[11:30:53]:/space/test_example$ python -m nose2 -c unittest.cfg --no-user-config
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
----------- coverage: platform linux, python 3.5.2-final-0 -----------
Name                    Stmts   Miss Branch BrPart  Cover
---------------------------------------------------------
my_module/__init__.py       0      0      0      0   100%
my_module/main.py           4      1      2      1    67%
---------------------------------------------------------
TOTAL                       4      1      2      1    67%

As you can see, it has given me Branch cover, but not told me the missing branches.

If I simply run coverage report (on the produced .coverage file that is left behind after running nose)

(example_venv) andy@batman[11:34:15]:/space/test_example$ coverage report
Name                    Stmts   Miss Branch BrPart  Cover   Missing
-------------------------------------------------------------------
my_module/__init__.py       0      0      0      0   100%
my_module/main.py           4      1      2      1    67%   3, 2->3
-------------------------------------------------------------------
TOTAL                       4      1      2      1    67%

You can see that coverage has given me the missing branches.


I can tell that coverage is picking up the .coveragerc when run under nose2, because if I remove the branch = True line under the [run] section in .coveragerc, then the branch coverage stats disappear from the report when run under nose.


Am I missing something extra I'm supposed to have done to get this show_missing config to get passed to coverage when it is run under nose2?

versions installed

cov-core==1.15.0
coverage==4.3.4
nose2==0.6.5

Solution

  • tl;dr - have to add coverage-report = term-missing to the [coverage] section in the unittest.cfg (or other --config [CONFIG], -c [CONFIG] file) for nose2. In order for Cov-Core to pass show_missing = True to coverage


    Longer explanation:

    When running coverage report on the command-line, show_missing is passed into coverage.coverage()* as None unless overridden in the command line arguments.

    However nose2 uses cov-core, which will pass show_missing to coverage.coverage()* in cov_core.CovController.summary() as either True or False. Based upon the following logic:

    if 'term' in self.cov_report or 'term-missing' in self.cov_report:
        show_missing = 'term-missing' in self.cov_report
        self.cov.report(show_missing=show_missing, ignore_errors=True, file=stream)
    

    cov_report is that coverage-report config set in nose2.

    So this is my confusion because I was trying to use coverage's configs to yield missing lines/branches (when doing a report to term, there's an extra flag for show_missing), when Cov-Core treats them as separate reports (either report term to get plain coverage, or term-missing to get plain coverage + missing lines).

    So don't let yourself be bitten by this bit of coverage behaviour Cov-Core has decided to override, when most other bits (include/exclude branch coverage for one) are still driven by coverage's configs.


    *coverage.coverage() is a backwards compatability tweak, and is actually an import (in coverage/__init__.py) for coverage.control.Coverage()