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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions