nikkie-ftnextの日記

イベントレポートや読書メモを発信

pytestのフィクスチャを共有したいときはconftest.pyを使いましょう(ゆめゆめimportしようとなされるな)

結論(『テスト駆動Python 第2版』より)

複数のテストファイルでフィクスチャを共有したい場合はconftest.pyファイルを使う必要があります。(Kindle版 p.95)

第3章でフィクスチャが詳しく解説されています

目次

複数のテストファイルでフィクスチャを共有したいケース

自明なテストを例に1、フィクスチャの共有に焦点を当てていきます。

2つのテストファイルを配置

.
└── tests/
    ├── __init__.py  # touchしただけ
    ├── test_1.py
    └── test_2.py

test_1.py

def test_1_equals_1():
    assert 1 == 1

test_2.py

def test_2_equals_2():
    assert 2 == 2

1つのファイルにフィクスチャを追加。別のファイルではエラー

test_1.pyにフィクスチャを追加します

import pytest


@pytest.fixture
def common():
    print("Run common fixture")


def test_1_equals_1(common):
    assert 1 == 1

test_2.pyでもこのフィクスチャを使おうとします

-def test_2_equals_2():
+def test_2_equals_2(common):
    assert 2 == 2

pytest -svを実行すると

tests/test_1.py::test_1_equals_1 Run common fixture
PASSED
tests/test_2.py::test_2_equals_2 ERROR

===================================== ERRORS ======================================
________________________ ERROR at setup of test_2_equals_2 ________________________
file /.../tests/test_2.py, line 1
  def test_2_equals_2(common):
E       fixture 'common' not found

test_2.pyではcommonフィクスチャが見つからずにエラーです

conftest.pyにフィクスチャを定義して解決

.
└── tests/
    ├── __init__.py
+    ├── conftest.py
    ├── test_1.py
    └── test_2.py

conftest.py

import pytest


@pytest.fixture
def common():
    print("Run common fixture")

test_1.py

def test_1_equals_1(common):
    assert 1 == 1

test_2.py

def test_2_equals_2(common):
    assert 2 == 2

pytest -svが通ります!

tests/test_1.py::test_1_equals_1 Run common fixture
PASSED
tests/test_2.py::test_2_equals_2 Run common fixture
PASSED

なお『テスト駆動Python』には、conftest.pyもimportすべきでないとあります(pytestにより自動で読み込まれるため)

importでテストは実行できるけれど

私の立場は「conftest.pyを置いて解決すべき」です。
『テスト駆動Python』や(後述する)pytestのドキュメントではconftest.pyが案内されているので、importを使うのは公式から外れたハックであり、可能な限り避けるべきと考えます。
動けばいいという妥協ではなく、ハックなく動作するコードを目指したいですね。

test_2.pyを以下のように変えるとエラーはなくなりますが、決しておすすめはしません。
conftest.pyを使いましょう

from tests.test_1 import common


def test_2_equals_2(common):
    assert 2 == 2

pytestのドキュメントより

「How to use fixtures」の以下
https://docs.pytest.org/en/8.0.x/how-to/fixtures.html#scope-sharing-fixtures-across-classes-modules-packages-or-session

ここでフィクスチャをconftest.pyに切り出すという話が出てきます。
ですが、この箇所はフィクスチャのスコープを扱っていて、conftest.pyについてはあまり言及がありません

「Fixture reference」の中の「conftest.py: sharing fixtures across multiple files」がズバリな項目でした。
https://docs.pytest.org/en/8.0.x/reference/fixtures.html#conftest-py-sharing-fixtures-across-multiple-files

The conftest.py file serves as a means of providing fixtures for an entire directory.

意訳「conftest.pyファイルはディレクトリ全体にフィクスチャを提供する手段に使える」

Fixtures defined in a conftest.py can be used by any test in that package without needing to import them (pytest will automatically discover them).

意訳「conftest.pyに定義されたフィクスチャは、そのパッケージのどのテストにおいても、フィクスチャのインポートの必要なく使える(pytestが自動でフィクスチャを見つける)」

詳細な図示もされています。

この箇所での例から、conftest.pyはディレクトリごとに置けることを知りました。

.
└── tests/
    ├── __init__.py
    ├── conftest.py
    ├── test_top.py
    └── subpackage/
        ├── __init__.py
        ├── conftest.py
        └── test_subpackage.py

終わりに

pytestのフィクスチャを共有したいときはconftest.pyを使うことについて見てきました。
「テストファイルからフィクスチャをimportしてはいけません」

『テスト駆動Python』によると、conftest.pyはローカルプラグインなるもののようです。
読み進めていけば理解が深まりそうです


  1. 「pytestのフィクスチャは、引数にある別のフィクスチャを実行できる」を確認したときと同じ例です