Search code examples
pythonpytestcode-coverage

pytest coverage 'if 0:' statement body not listed as a miss


Test marks the code as covered if the condition is 0 but as uncovered if the condition is a variable with value of zero.

I was trying a simple thing in pytest with coverage and I found this bug (?). I am not sure if I am missing something in how pytest or python works.

Here bellow is my function

def dummy_func(a=0):
    
    if a:
        print('this part is not tested !!')
    else:
        print('this part is tested !!')
    
    if 0: # tried None as well  
        print('this part is not tested, but appears like it is !') 
    else:
        print('this part is tested !!')

return 1

and this is the report I got


---------- coverage: platform linux, python 3.10.6-final-0 -----------
Name                     Stmts   Miss  Cover   Missing
------------------------------------------------------
myproject/flask_api.py       7      1    86%   4
tests/test_hello.py          4      0   100%
------------------------------------------------------
TOTAL                       11      1    91%

Should not the line under the if 0 be marked as Miss. Is that a bug or I am missing something ?

I got that in both version

pytest-cov = "4.0.0" and "3.0.0" also with coverage

my test code is that

from myproject import flask_api

def test_dummy():
    result = flask_api.dummy_func()

    assert result == 1

Solution

  • So the answer to your question is that it's not a bug, it's expected behavior.

    From the coverage.py docs:

    After your program has been executed and the line numbers recorded, coverage.py needs to determine what lines could have been executed. Luckily, compiled Python files (.pyc files) have a table of line numbers in them. Coverage.py reads this table to get the set of executable lines, with a little more source analysis to leave out things like docstrings.

    and

    The data file is read to get the set of lines that were executed. The difference between the executable lines and the executed lines are the lines that were not executed.

    and

    The same principle applies for branch measurement, though the process for determining possible branches is more involved. Coverage.py uses the abstract syntax tree of the Python source file to determine the set of possible branches.

    That is just how coverage.py works. It does not consider unreachable code in its report. It doesn't mark it as missed but it doesn't mark it as tested either.

    For instance, here is how the report looks in PyCharm (note the unmarked line 7):

    enter image description here

    Some more examples:

    enter image description here

    Interestingly enough, PyCharm can't evaluate 'a' * 0 as "always falsy" but can evaluate 'a'.replace('a', '') as such, while coverage.py does the opposite.