フィクスチャについて

pytest のフィクスチャは、明示的でモジュール化可能でスケーラブルになるように設計されています。

フィクスチャとは

テストにおいて、フィクスチャ はテストのために定義された信頼性の高い一貫したコンテキストを提供します。 これには環境 (既知のパラメーターで設定されたデータベースなど) やコンテンツ (データセットなど) が含まれます。

フィクスチャはテストの arrange フェーズを構成するステップとデータを定義します (テストの構造 を参照)。 pytest では、この目的のために定義する関数です。 テストの act フェーズを定義するためにも使用でき、より複雑なテストを設計するための強力なテクニックです。

フィクスチャによって設定されたサービス、状態、その他の動作環境は、引数を通してテスト関数からアクセスします。 テスト関数で使用される各フィクスチャには、通常、テスト関数の定義にフィクスチャの名前を付けたパラメーターがあります。

ある関数がフィクスチャであることを pytest に伝えるには、@pytest.fixture でデコレートします。 以下は pytest におけるフィクスチャの簡単な例です:

import pytest


class Fruit:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name


@pytest.fixture
def my_fruit():
    return Fruit("apple")


@pytest.fixture
def fruit_basket(my_fruit):
    return [Fruit("banana"), my_fruit]


def test_my_fruit_in_basket(my_fruit, fruit_basket):
    assert my_fruit in fruit_basket

テストは単一のフィクスチャに限定される必要はありません。 必要な数のフィクスチャに依存でき、フィクスチャは他のフィクスチャも使用できます。 これが pytest のフィクスチャシステムの真価が発揮される点です。

xUnit スタイルのセットアップ/ティアダウン関数からの改善点

pytest のフィクスチャは、従来の xUnit スタイルのセットアップ/ティアダウン関数から劇的な改善を提供します:

  • フィクスチャには明示的な名前があり、テスト関数、モジュール、クラス、またはプロジェクト全体からその使用を宣言することで有効化されます。

  • フィクスチャはモジュール式に実装され、各フィクスチャ名は他のフィクスチャを使用できる フィクスチャ関数 をトリガーします。

  • フィクスチャの管理は単純なユニットから複雑な機能テストまでスケールし、設定やコンポーネントのオプションに応じてフィクスチャとテストをパラメーター化したり、関数、クラス、モジュール、またはテストセッション全体のスコープでフィクスチャを再利用したりできます。

  • ティアダウンのロジックは、使用されるフィクスチャの数に関係なく、エラーを手動で慎重に処理したり、クリーンアップステップの追加順序を細かく管理したりする必要なく、簡単かつ安全に管理できます。

さらに、pytest は xunitスタイルのセットアップを実装する方法 のサポートを継続しています。 好みに応じて、従来のスタイルから新しいスタイルに段階的に移行しながら、両方のスタイルを混在させることができます。 既存の unittest.TestCase スタイル から始めることもできます。

フィクスチャのエラー

pytest は、与えられたテストのすべてのフィクスチャを直線的な順序で配置し、どのフィクスチャが最初、2 番目、3 番目などに実行されるかを把握しようとします。 ただし、先行するフィクスチャに問題があり例外が発生した場合、pytest はそのテストのフィクスチャの実行を停止し、テストにエラーがあるとマークします。

テストにエラーがあるとマークされた場合でも、それはテストが失敗したということではありません。 依存するものの 1 つに問題があったため、テストを試みることすらできなかったということを意味します。

これは、与えられたテストに対して可能な限り不要な依存関係を排除することが良い理由の 1 つです。 そうすることで、無関係なものの問題が、何が問題を抱えているかもしれないという不完全な状況を引き起こすことを防ぎます。

説明に役立つ簡単な例を示します:

import pytest


@pytest.fixture
def order():
    return []


@pytest.fixture
def append_first(order):
    order.append(1)


@pytest.fixture
def append_second(order, append_first):
    order.extend([2])


@pytest.fixture(autouse=True)
def append_third(order, append_second):
    order += [3]


def test_order(order):
    assert order == [1, 2, 3]

何らかの理由で order.append(1) にバグがあり例外が発生した場合、order.extend([2])order += [3] にも問題があるかどうかを知ることはできません。 append_first が例外を投げた後、pytest は test_order のためのフィクスチャをそれ以上実行せず、test_order 自体の実行も試みません。 実行されるのは orderappend_first だけです。

テストデータの共有

ファイルからのテストデータをテストで利用可能にしたい場合、そのデータをフィクスチャにロードしてテストで使用するのが良い方法です。 これにより pytest の自動キャッシュメカニズムを利用できます。

もう 1 つの良いアプローチは、tests フォルダにデータファイルを追加することです。 テストのこの側面の管理を支援するコミュニティプラグインも利用可能です。 例えば pytest-datadirpytest-datafiles などがあります。

フィクスチャのクリーンアップに関する注意

pytest は SIGTERMSIGQUIT シグナルに対して特別な処理を行いません (SIGINT は Python ランタイムによって KeyboardInterrupt を介して自然に処理されます)。 そのため、これらのシグナルによって Python プロセスが終了したときにクリアする必要がある外部リソースを管理するフィクスチャでは、リソースがリークする可能性があります。

pytest がフィクスチャのクリーンアップを行うためにこれらのシグナルを処理しない理由は、シグナルハンドラーがグローバルであり、それらを変更すると実行中のコードに干渉する可能性があるためです。

これらのシナリオで終了に関して特別な注意が必要なフィクスチャがある場合は、issue トラッカーの このコメント で可能な回避策を参照してください。