pytest Exit Code 5 "No Tests Collected" - Causes & Fix in CI
pytest exits with code 5 when it collected zero tests. CI treats that as a failure on purpose - a run that tests nothing should not look green. Usually discovery is pointed at the wrong place or your names don’t match the conventions.
What this error means
pytest prints "no tests ran" and "collected 0 items", and the job fails with exit code 5. Your tests exist, but pytest did not discover any from where it ran.
============================ no tests ran in 0.02s =============================
ERROR: file or directory not found: tests/
# or
collected 0 items
$ echo $?
5Diagnose it: what did pytest actually collect?
Most pytest failures that only happen in CI are collection or import-path problems rather than test failures. The runner has a different working directory, a different sys.path, and usually no editable install, so a test module that imports your package locally may not resolve at all.
# what would run, without running it
pytest --collect-only -q | tail -20
# where pytest thinks the root is (drives conftest and import mode)
pytest --collect-only 2>&1 | grep -i rootdir
# is the package importable at all from the runner cwd?
python -c "import yourpackage, sys; print(yourpackage.__file__)"
python -c "import sys; print(sys.path)"Common causes
Running from the wrong directory or path
CI runs pytest from a directory where the test path doesn’t exist, or passes a path that doesn’t match where tests live.
Tests don’t match discovery rules
pytest only collects files like test_*.py/*_test.py and functions named test_*. Misnamed files or classes without the Test prefix are invisible.
A marker filter or -k deselected everything
A -m/-k expression, or addopts in config, can filter out every test, leaving zero collected.
How to fix it
Confirm what pytest sees
List collection without running to see exactly which tests are discovered.
pytest --collect-only
pytest --collect-only tests/ # point at the right dirFix discovery naming and config
- Name files
test_*.pyand functionstest_*(classesTest*with no__init__). - Set
testpathsinpyproject.toml/pytest.iniso CI and local agree. - Check
addopts,-m, and-karen’t deselecting everything.
Allow no-tests if intentional
If a subset legitimately has no tests, suppress the exit-5 failure for that step.
pytest tests/optional || [ $? -eq 5 ]Make a zero-test run fail the build
# fail explicitly when nothing is collected
pytest --strict-markers -q
test "${PIPESTATUS[0]}" -ne 5 || { echo "pytest collected no tests"; exit 1; }How to prevent it
- Set
testpathsso discovery is explicit and portable. - Follow pytest naming conventions for files, classes, and functions.
- Treat exit 5 as a signal that collection broke, not noise.