📦

pytest 用リソースがテストコードと一緒に置いてあると邪魔じゃないですか?

に公開

テスト用リソースがテストコードと一緒に置いてあると邪魔じゃないですか?

テスト用リソースを使用するテストはそれほど多くありません

テストコード内にテスト用リソースを配置すると、
エクスプローラーのツリー表示領域がテスト用リソースで埋め尽くされてしまいます:

tests/
└── some_test_package/
       ├── some_test_module_a/    (テスト用リソース)
       ├── some_test_module_b/    (テスト用リソース)
       ├── some_test_module_c.txt (テスト用リソース)
       ├── some_test_module_a.py
       ├── some_test_module_a.py
       ├── some_test_module_c.py
       ├── some_test_module_d.py
       └── some_test_module_e.py

通常時にテストに関係のないファイルやディレクトリーは、
別の場所に隔離して普段は表示しないようにした方が生産性は高まるのではないでしょうか:

tests/
├── some_test_package/
│     ├── some_test_module_a.py
│     ├── some_test_module_b.py
│     └── some_test_module_c.py
│     └── some_test_module_d.py
│     └── some_test_module_e.py
└── testresources/ (テスト用リソース)

そこで、テスト用リソースをテストコードと分離して配置して管理しつつ
テストコードから簡単にアクセスすることが簡単にできる
pytest-resource-path という pytest プラグインを作成しました

pytest-resource-path

pytest-resource-path は分離されたテスト用リソースディレクトリーへのファイルシステムパスを
pytest fixture として提供します

https://pypi.org/project/pytest-resource-path/

(pytest fixture については理解していなくてもなんとか使えると思います)

この pytest プラグインはデフォルトでは、
テストリソースが tests/testresources に配置されていることを前提としています
(これらのディレクトリー名は pyproject.tomlpytest.ini などでカスタマイズできます)

tests/
├── some_test_package/
│     └── some_test_module.py
└── testresources/
       └── some_test_package/
              └── some_test_module/

これによりテスト用リソースが隔離され、
テストコードのツリーがテスト用リソースで散らかることはなくなります

インストール方法

pip install pytest-resource-path

使用法

基本

まず実装コードを示します、たったこれだけになります:

def test_method(resource_path):
    text_test_resource = (resource_path / 'test_resource.txt').read_text()

上記のように、pytest フィクスチャ: resource_path を使用できます

  • これは pathlib.Path インスタンス (絶対パス) です

上記のメソッド: test_method()
tests/some_tests_package/some_test_module.py にあると仮定すると、
上記のテストコード中で読み取っているテスト用ファイル: test_resource.txt
次のディレクトリーに配置する必要があります:

tests/
├── some_test_package/
│     └── some_test_module.py
└── testresources/
       └── some_test_package/
              └── some_test_module/
                     └── test_method/
                            └── test_resource.txt

メソッドごとにディレクトリーを省略したい場合は、次のようにします:

def test_method(resource_path):
    text_test_resource = Path(f'{resource_path}.txt').read_text()

この場合、テスト用リソースのファイル名は test_method.txt となり、
次のディレクトリーに配置する必要があります:

tests/
├── some_test_package/
│     └── some_test_module.py
└── testresources/
       └── some_test_package/
              └── some_test_module/
                     └── test_method.txt

クラス名はパスでは使用されないことに注意してください
(冗長で使いにくくなると思われるため、そのような設計にしています)

テストリソースのルートディレクトリーへのパスを取得する方法

testresources を指す resource_path_root フィクスチャを使用できます:

def test_method(resource_path_root):
    text_test_resource = (resource_path_root / 'test_resource.txt').read_text()

上記の場合、テスト用リソース: test_resource.txt
次のディレクトリーに配置する必要があります:

tests/
├── some_test_package/
│     └── some_test_module.py
└── testresources/
       └── test_resource.txt

この resource_path_root は、ディレクトリー構造の移行期間に役立つかもしれません

また、いくつかのテストで共通のディレクトリーを準備するのにも役立ちます:

def test_method(resource_path_root):
    text_test_resource = (resource_path_root / 'common/test_resource.txt').read_text()
tests/
├── some_test_package/
│     └── some_test_module.py
└── testresources/
       └── common/
              └── test_resource.txt

ディレクトリー名をカスタマイズする方法

このプラグインでは、
ディレクトリー構造からファイルを取得するために、
ディレクトリー名を定められた名前にする必要があります

デフォルトは次の通りです:

ディレクトリーの種類 定められた名前
テストコードのディレクトリー tests
テスト用リソースのディレクトリー testresources

これらのディレクトリー名は、pyproject.tomlpytest.ini などでカスタマイズできます

例として、次のようなディレクトリー構造にしたい場合を考えます:

integrationtests/
├── some_test_package/
│     └── some_test_module.py
└── data/
       └── some_test_package/
              └── some_test_module/

pyproject.toml の場合

pyproject.toml
[tool.pytest.ini_options]
"resource-path.directory-name-tests" = "integrationtests"
"resource-path.directory-name-test-resources" = "data"

pytest.ini の場合

pytest.ini
[pytest]
resource-path.directory-name-tests = integrationtests
resource-path.directory-name-test-resources = data

Discussion