Skip to content

Why doesn't pytest-cov fail with a non-zero exit code when code coverage threshold isn't met, when running on multiple directories? #728

@samdewr

Description

@samdewr

Summary

Expected vs actual result

When the average test coverage threshold across multiple modules isn't met, pytest doesn't fail with a non-zero exit code, even though it should.

Reproducer

Versions

OS and hardware

  Model Name:	MacBook Pro
  Model Identifier:	Mac16,5
  Chip:	                 Apple M4 Max
  System Version:	macOS 15.6 (24G84)
  Kernel Version:	Darwin 24.6.0

Pytest versions

pytest = "^8.3.4"
pytest-cov = "^6.0.0"

Config

In pyproject.toml:

[tool.coverage.run]
branch = true

[tool.coverage.report]
fail_under = 75
exclude_lines = ['pragma: no cover', 'raise NotImplementedError', '\s*\.\.\.']

[tool.coverage.html]
directory = './coverage'

[tool.coverage.xml]
output = './coverage/cov.xml'

Code

My command:

❯ pytest --cov module1 --cov module2 --cov module3 --cov-report --cov-fail-under=75

I get the following result:

FAIL Required test coverage of 75.0% not reached. Total coverage: 74.79%

But the exit code remains zero:

echo $?
0

As a result, my CI/CD pipeline (triggered upon a PR to main) passes just fine even though my coverage threshold isn't met. This causes my main branch to have too low test coverage.

I've tried

  • Passing --cov=module1,module2,module3 instead of having one pytest call with three different --cov arguments. This causes the coverage report to fail altogether, because it says that CoverageWarning: Module module1,module2,module3 was never imported. I.e., it tries to import module1,module2,module3 as a single module and it fails, since they are separate modules.
  • Passing the to-be-included modules in pyproject.toml under [tool.coverage.run] in the include argument. Problem persists: test coverage threshold failure is noted, but exit code is zero.
  • Interestingly, when you test on each of the modules individually, you do get a non-zero exit code for the module that has too low coverage.
  • On the internet, I can't really seem to find a working solution. Copilot isn't very useful here either.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions