1
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?

はじめに

VS CodeのTest Explorerでpytestを使っていると、テスト自体は存在するのに、次のようなエラーで検出されないことがあります。

ModuleNotFoundError: No module named 'app'

この問題を調べていると、次の2つのコマンドで結果が変わることがあります。

pytest
python -m pytest

どちらもpytestを実行していますが、Pythonのモジュール探索パスであるsys.pathが同じとは限りません。

今回は、最小構成で違いを確認し、VS Code側でどこを見るべきか整理します。

最小構成を作る

ディレクトリ構成は次のとおりです。

pytest-path-example/
├── app.py
└── tests/
    └── test_app.py

app.pyには、単純な足し算関数を作ります。

app.py
def add(a: int, b: int) -> int:
    return a + b

テストは次のようにします。

tests/test_app.py
from app import add


def test_add():
    assert add(2, 3) == 5

pytestでは失敗する

プロジェクト直下でpytestを実行します。

pytest -q

手元の環境では、次のようになりました。

E   ModuleNotFoundError: No module named 'app'

1 error

一方、次のコマンドでは成功しました。

python -m pytest -q
.                        [100%]
1 passed

同じpytestなのに結果が異なりました。

なぜ結果が変わるのか

違いは、pytestをどのように起動しているかです。

pytest

こちらは、環境にインストールされたpytestという実行ファイルを直接呼び出します。

一方で、

python -m pytest

は、現在選択されているpythonからpytestモジュールを起動します。この実行方法では、現在のディレクトリがPythonのモジュール探索対象に追加されます。

そのため、プロジェクト直下にあるapp.pyをimportできました。

つまり、問題はテストコードではなく、次のどちらかかもしれません。

使っているPythonが違う
モジュールを探す場所が違う

実行環境を確認する

まず、Pythonの実体を確認します。

python -c "import sys; print(sys.executable)"

次にpytestの場所を確認します。

macOSまたはLinuxでは、次のコマンドを使えます。

which python
which pytest

Pythonから見えているモジュール探索パスも確認できます。

python -c "import sys; print(*sys.path, sep='\n')"

仮想環境を切り替えたあとにVS Codeを開いた場合などは、pythonpytestが別の環境を指していることがあります。

VS Code側で確認する場所

VS Codeでは、まずコマンドパレットから次を実行します。

Python: Select Interpreter

ここで、ターミナルから確認したPythonと同じ環境を選びます。

pytestの基本設定は次のようになります。

.vscode/settings.json
{
    "python.testing.pytestEnabled": true,
    "python.testing.unittestEnabled": false,
    "python.testing.pytestArgs": [
        "tests"
    ]
}

テストが検出されない場合は、次の画面を開きます。

表示
→ 出力
→ Python

Test Explorerに表示される短いエラーだけでなく、テスト検出時の詳しいログを確認できます。

cwdとrootdirはPYTHONPATHではない

VS Codeには、テストの作業ディレクトリを指定する設定もあります。

{
    "python.testing.cwd": "${workspaceFolder}"
}

ただし、ここで注意が必要です。

pytestのrootdirは、設定ファイルの探索やテストIDの基準として使われる値です。rootdirを変更しただけで、対象ディレクトリが自動的にsys.pathへ追加されるわけではありません。

つまり、次の3つは別のものです。

cwd       コマンドを実行する現在位置
rootdir   pytestが認識するプロジェクトの基準位置
sys.path  Pythonがimport先を探す場所

cwdrootdirだけを何度も変更しても直らない場合は、sys.pathを確認した方が早いです。

小規模プロジェクトでの修正方法

単純な構成なら、pyproject.tomlにPythonパスを明示できます。

pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]

これで、次のコマンドでもプロジェクト直下のapp.pyをimportできます。

pytest -q
1 passed

ただし、本格的なPythonパッケージでは、srcレイアウトを使ってパッケージとしてインストールする方が安全です。

project/
├── pyproject.toml
├── src/
│   └── myapp/
│       ├── __init__.py
│       └── core.py
└── tests/
    └── test_core.py

開発中はeditable installを使います。

python -m pip install -e .
python -m pytest

この方法なら、VS Code、ターミナル、CIで同じimport規則を使いやすくなります。

testファイルでsys.pathを書き換えない

一時的な回避策として、テストファイルに次のようなコードを書きたくなります。

import sys
sys.path.append("..")

しかし、これはテストを実行した場所によって挙動が変わります。

テストコード内でパスを修正するのではなく、次のいずれかで管理する方が再現性があります。

pyproject.tomlでpythonpathを指定する
パッケージをeditable installする
python -m pytestで実行する

まとめ

VS Codeでpytestが検出されないとき、確認すべきなのはpytestEnabledだけではありません。

python -c "import sys; print(sys.executable)"
python -c "import sys; print(*sys.path, sep='\n')"

pytest -q
python -m pytest -q

この4つを比較すると、Python環境の違いなのか、importパスの違いなのかを切り分けられます。

VS CodeのTest Explorerは便利ですが、実際にテストを動かしているのはPythonとpytestです。

「どのPythonが、どのディレクトリから、どこを探しているか」を確認することが、テスト検出エラーを解決する近道でした。

1
0
0

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
1
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?