0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pytest で同名テストファイルが ImportPathMismatchError になる原因と解決策

0
Last updated at Posted at 2026-06-30

pytest で同名テストファイルが ImportPathMismatchError になる原因と解決策

複数のディレクトリにあるテストをまとめて流したら、収集の段階でこんなエラーが出て止まりました。

==================================== ERRORS ====================================
___________ ERROR collecting component-b/tests/test_completeness.py ____________
import file mismatch:
imported module 'test_completeness' has this __file__ attribute:
  <path>/component-a/tests/test_completeness.py
which is not the same as the test file we want to collect:
  <path>/component-b/tests/test_completeness.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules

import file mismatch: から始まるこの出力は、pytest 内部では ImportPathMismatchError という例外として投げられます(ソースは _pytest/pathlib.py)。
これを見て、どう直せばいいか調べた記録です。同じ画面で手が止まった人向けに書いています。

この記事で分かることは次の3点です。

  • なぜ別ディレクトリの同名テストファイルが衝突するのか
  • いちばん手早い直し方(実機で確認済み)
  • ほかの直し方との使い分け(リネーム不要の手も含む)

検証は pytest 9.1.1 / Python 3.13 で、実際に再現と解消を確認しました。

何が起きたか

複数のコンポーネントにそれぞれテストを置いた、こんな構成でした。

project/
  component-a/tests/test_completeness.py
  component-b/tests/test_completeness.py

中身は別物ですが、ファイル名はどちらも test_completeness.py です。
プロジェクトのルートから、両方のディレクトリをまとめて指定して実行します。

pytest component-a/tests/ component-b/tests/

すると、片方は収集できるのにもう片方の収集で冒頭の import file mismatch: が出て、テストが1件も走らないまま止まりました。
テストコード自体には文法エラーも import エラーもありません。
pytest component-a/tests/ だけ、あるいは pytest component-b/tests/ だけなら、それぞれ問題なく通ります。両方を同時に集めようとしたときだけ落ちる、という症状でした。

なぜ起きるのか

原因は、pytest がデフォルトの import モード(prepend)で「テストファイルのあるディレクトリを sys.path の先頭に挿し込み、ファイル名(basename)をそのままトップレベルのモジュール名として import する」ためです1

__init__.py を置いていないテストファイルは、basename がモジュール名になります。
component-a/tests/test_completeness.pycomponent-b/tests/test_completeness.py も、どちらも test_completeness という同じモジュール名で取り込まれます。
先に片方が test_completeness として import 済みになっているところへ、別ファイルを同じ名前で取り込もうとして「同じモジュール名なのに __file__ が違う」と弾かれる、というわけです。

どう直したか

ファイル名にコンポーネント名のプレフィックスを付けてリネームしたら、それだけで通りました。

project/
  component-a/tests/test_a_completeness.py
  component-b/tests/test_b_completeness.py

basename が test_a_completenesstest_b_completeness で別物になるので、モジュール名がぶつかりません。

============================== 2 passed ===============================

ルートに置いた conftest.py の fixture も、リネーム後そのまま解決できました(実機で確認)。
副次的に、テスト名を見ればどのコンポーネントのものか分かるようになります。
tests/ 配下にずらりと並んだときの見通しも良くなりました。
構成を変えずファイル名だけ変えればいいので、まず試す手としてはこれが手軽でした。

リネームせずに直すなら

ファイル名を変えたくないときは、テストのディレクトリを Python パッケージにする手もあります。
各ディレクトリに空の __init__.py を置くと、そのディレクトリはパッケージ扱いになり、モジュール名が basename 単体ではなく階層込みの完全修飾名(例: component_a.tests.test_completeness)になります。
名前にディレクトリ構造が入るため、別ディレクトリの同名ファイルとぶつかりません。
※完全修飾名がコンポーネント間で別物になるところまで、親ディレクトリにも __init__.py を置きます。

pytest 公式もテストをパッケージとして並べることを推奨しています。
設定を変えてよければ、--import-mode=importlib にすると sys.path を触らない取り込み方になり、basename の重複自体が問題になりません。
(コメントいただきありがとうございました)

次に同じ画面を見たら

import file mismatch: を見たら、まず「複数のディレクトリに同名のテストファイルがないか」を考える。

横展開でテストをコピーしていくとき(コンポーネントごとに同じ test_xxx.py を増やすときなど)は、最初からファイル名にコンポーネント名を入れておくと、この衝突を踏まずに済みます。

参考リンク

  1. pytest documentation — Import modes / pythonpath(prepend・importlib の機序と制約、既定の import モード)

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?