Consider the following simple program:
# thingy.py
import sys
print("ready")
for line in sys.stdin:
print(line, end='')
If I want to unit-test the program, I can stub out the side-effecting functions ready and print easily enough.
However, if I want to perform an end-to-end test, I want to run the real program and test real responses to real inputs.
I can test it easily enough:
# test_thingy.py
import pytest
import subprocess
import sys
@pytest.fixture
def thingy_instance():
proc = subprocess.Popen(
[sys.executable, "-um", "thingy"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
encoding="utf-8")
for line in proc.stdout:
if "ready" in line:
break
yield proc
proc.terminate()
proc.wait()
def test_thingy(thingy_instance):
stdout, stderr = thingy_instance.communicate("Hello!")
assert stdout == "Hello!"
# more tests
However, if I try to generate coverage for this, it doesn't work:
$ coverage run --source . -m pytest
============================= test session starts ==============================
platform linux -- Python 3.10.12, pytest-7.1.3, pluggy-1.0.0
rootdir: /home/user/playground-local/python/coverage-test-stuff
plugins: Faker-16.6.1, smtpdfix-0.4.0, quarantine-2.0.0, cov-3.0.0, mock-3.8.2, docker-tools-3.1.3, postgresql-4.1.1
collected 1 item
test_thingy.py . [100%]
============================== 1 passed in 0.09s ===============================
$ coverage report
Name Stmts Miss Cover
------------------------------------
test_thingy.py 15 0 100%
thingy.py 4 4 0%
------------------------------------
TOTAL 19 4 79%
Running coverage run -m thingy generates coverage.
Running coverage run --source . -m pytest doesn't generate coverage.
Replacing the Popen command with a coverage run -m command doesn't generate coverage.
The answer is to use the
-pflag forcoverage run, then runcoverage combineafter the fact.From the documentation:
To get full coverage, replace the
Popenpython command with the coverage command, passing-p:And execute the top-level
coveragecommand with-p:Then run
coverage combine:Finally, the
coverage report(as well as coverage generation commands) will include all sub-process runs: