The error reads import file mismatch: imported module 'test_invoice' has this __file__ attribute … which is not the same as the test file we want to collect, and it usually appears the day someone adds a test file whose name already exists elsewhere in the suite. It is not a bug in pytest and not random: it is pytest noticing that two different files are trying to become the same Python module, and refusing to run the wrong one silently.
The fix is quick once the mechanism is clear, and the durable version of the fix — switching import mode — also removes the class of problem for good. This guide walks through the cause, the two fixes, and how to confirm the layout is sound afterwards.
Prerequisites
pytest >= 8.0;--import-mode=importlibhas existed since 6.0 but is reliable for most layouts from 8.- Shell access to the repository to list and inspect files.
- The layout background in test layout and import modes.
Solution
First rule out stale bytecode, then find the collision, then remove it.
# 1. Stale .pyc files from moved or renamed tests can trigger the same error.
find . -name "__pycache__" -type d -prune -exec rm -rf {} +
# 2. Which basenames occur more than once?
find tests -name "test_*.py" -printf "%f\n" | sort | uniq -d
test_invoice.py
test_models.py
# 3a. The durable fix: import by path, no module-name collisions at all.
[tool.pytest.ini_options]
addopts = "--import-mode=importlib"
# 3b. The alternative under prepend mode: make every directory a package.
find tests -type d -exec touch {}/__init__.py \;
The second command is shown for completeness. Under the default prepend mode it works, because it changes the module names from test_invoice to tests.billing.test_invoice and tests.api.test_invoice. But it turns the test tree into an importable package called tests, which can collide with other distributions and has to be excluded from the built wheel. The configuration change avoids all of that and is the one to prefer.
Why this works
Under the default prepend import mode, pytest walks up from each test file for as long as it finds __init__.py, stops at the first directory without one, inserts that directory at the front of sys.path, and imports the file under the dotted name formed from its path relative to that directory. Two files named test_invoice.py in directories with no __init__.py both resolve to the bare name test_invoice. The first is imported and cached in sys.modules; the second finds the name already taken by a different file, and pytest raises rather than reuse the wrong module.
--import-mode=importlib does not consult sys.path or derive names from the directory structure. It imports each file directly by location and gives it a unique name, so two files with the same basename are simply two modules. That is why it removes the error class entirely rather than working around one instance of it.
Edge cases and failure modes
- Tests that import each other by bare name. Under
importlib,from test_helpers import make_orderno longer works, because the test directory is not onsys.path. Move shared helpers into a proper module under aconftest.pyor an installed test-support package. - A partial set of
__init__.pyfiles. Adding them to some directories but not others changes where the walk stops, and can produce a new mismatch between files that previously had different names. It is all or nothing. - Stale
.pycafter a move. A test file renamed fromtest_a.pytotest_b.pycan leave a compiledtest_abehind that claims the old name. Clearing__pycache__rules it out in a second. - The same basename in
srcandtests. A test module named like an application module can shadow it underprepend. Another reason to prefer asrclayout andimportlib. - IDE runners with their own settings. An IDE that passes its own
--import-modeproduces different behaviour from the command line. Put the mode in configuration so every runner inherits it.
Reading the error message precisely
The full message carries more information than its first line suggests, and reading all of it usually identifies both files without any searching.
import file mismatch:
imported module 'test_invoice' has this __file__ attribute:
/repo/tests/billing/test_invoice.py
which is not the same as the test file we want to collect:
/repo/tests/api/test_invoice.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
The first path is the file that won — the one imported earlier and cached under the bare module name. The second is the one pytest was trying to collect when it noticed. The module name in quotes is the collision itself. If the two paths are the same file at different locations — the old and new home of a moved test — the cause is stale bytecode and clearing __pycache__ is the whole fix. If they are genuinely different files, the cause is layout, and the import mode is the fix.
CI runners that restore a cached virtual environment or workspace can carry stale bytecode between builds, which is why this error sometimes appears only in CI after a file move and never locally. Setting PYTHONDONTWRITEBYTECODE=1 in the CI environment, or excluding __pycache__ from the cache key's restored paths, removes that source permanently.
Choosing between the two fixes
Both fixes make the error go away, and they differ in what they leave behind.
The __init__.py route keeps the default import mode, which some older plugins and some cross-test imports depend on. Its cost is structural: the test tree becomes a package, every new directory must remember to include the marker file, and the package name tests is claimed in the import namespace. Forgetting one __init__.py in a new directory is enough to reintroduce the error months later, typically for whoever adds the next duplicate basename.
The importlib route changes one configuration line and removes the marker files. Its cost is behavioural: test modules can no longer import one another by bare name, and a very small number of plugins that assume prepend semantics need updating. For a new or medium-sized suite that cost is nearly zero; for a large legacy suite it is a few hours of moving shared helpers into proper modules, which is an improvement in its own right.
Confirming the layout is sound
After either fix, two checks confirm that the suite now collects the same tests however it is invoked.
Run collection from the repository root and from inside a test subdirectory, and compare the results. With testpaths and the import mode in configuration, they should be identical; if they differ, some part of the behaviour still depends on the current directory, and the next person to run a single file from their editor will see a different suite from CI.
Then add a new test file whose basename duplicates an existing one, run the suite, and delete it again. Under importlib it should simply collect. This takes thirty seconds and proves the fix addresses the class of problem rather than the instance that prompted it — which matters because the next duplicate basename will be added by someone who has never heard of this error and would otherwise spend an hour rediscovering it. Recording the chosen import mode and the reason in a comment beside the configuration completes the job, so that nobody later removes it as an unexplained setting.
Frequently Asked Questions
Why does the error mention pycache?
Because a compiled file from a previous location can also trigger the check. If a test file was moved or renamed, a stale .pyc may still claim the old module name. Deleting __pycache__ directories, or running with -p no:cacheprovider once, rules this out before investigating the layout.
Is adding init.py everywhere a real fix?
It is a working fix under the default prepend mode, because it makes module names unique by turning the test tree into packages. The cleaner fix is --import-mode=importlib, which removes the need for those files entirely and avoids the test tree becoming an importable package called tests.
Can two test files ever safely share a basename?
Yes, under importlib mode, or under prepend mode when both live inside packages so their dotted names differ. What cannot work is two same-named files in plain directories under prepend mode, because both become the top-level module test_name.
Related
- Test Layout & Import Modes — the full model behind this error.
- Using importmode=importlib Without init.py Files — making the durable fix work across a whole suite.
- Creating conftest.py Hierarchies for Monorepos — where duplicate basenames are most common.
- Cutting Collection Time with norecursedirs — keeping collection to the directories you mean.
← Back to Test Layout & Import Modes