Pytest フィクスチャ

完了

フィクスチャは、モジュール式、スケーラブル、保守可能なテストを作成するために使用される pytest ヘルパー関数です。 フィクスチャを使用して、データベース接続、テスト データの作成、テストを実行する前に必要なシステム状態の構成などのテストの前提条件を設定します。 テストの実行後のクリーンアップにも使用できます。

pytest フィクスチャの主な特性は次のとおりです。

  • スコープ制御: フィクスチャを呼び出す頻度を決定する scope パラメーター ( functionclassmodulesessionなど) を使用して、フィクスチャを異なるスコープに構成できます。
  • セットアップと破棄の処理: Pytest はフィクスチャのライフサイクルを管理し、必要に応じて自動的にセットアップおよび破棄します。
  • 依存関係の挿入: フィクスチャは引数としてテスト関数に挿入され、どのテストがどのフィクスチャに依存しているかが明確になります。
  • 再利用性とモジュール性: フィクスチャは 1 か所で定義でき、複数のテスト機能、モジュール、さらにはプロジェクト間で使用できます。

一時ファイルのフィクスチャの作成

ファイルを扱うテストを書く際には、テスト後にファイルシステムを乱雑にしないために、一時ファイルを使うことが一般的です。 pytest を使用すると、一時ファイルを設定するフィクスチャを作成できます。 このフィクスチャでは、Python の tempfile モジュールを使用して一時ファイルを安全に生成し、ローカル環境に影響を与えずに使用および削除できるようにします。 (このフィクスチャの初期バージョンでは、 delete=False フラグのため、ファイルは自動的に削除されません。後でファイルの削除に対処します)。)

フィクスチャは次のようになります。

import pytest
import tempfile

@pytest.fixture
def tmp_file():
    def create():
        # Create a named temporary file that persists beyond the function scope
        temp = tempfile.NamedTemporaryFile(delete=False)
        return temp.name
    return create

この設定で、tmp_file() はフィクスチャとして機能します。 フィクスチャの名前は、テストでそれがどのように参照されるかを示します。 フィクスチャ内では、入れ子になった関数 create() は、フィクスチャのセットアップ時ではなく、呼び出されたときにのみファイルを作成します。 これにより、一時ファイルが作成されるタイミングを正確に制御できます。これは、タイミングとファイルの状態が重要なテストで役立ちます。

入れ子になった関数 create()内に一時ファイルが作成され、そのファイルへの絶対パスが返されます。 私たちが記述したフィクスチャをテストで使用する方法の例を次に示します。

import os

def test_file(tmp_file):
    path = tmp_file()
    assert os.path.exists(path)

テストでは、フィクスチャの名前を引数として指定することでフィクスチャを使用できます。 ファイルに書き込んだり、アクセス許可や所有権の変更などの変更を行ったりすることで、簡単なユース ケースを簡単に拡張できます。

スコープ管理

pytest では、セットアップと破棄ルーチンを使用してテスト リソースのライフサイクルを管理することは、クリーンで効率的なテスト環境を維持するために重要です。 また、各テストが既知の一貫性のある状態で開始されるようにして、テストの整合性を保護することもできます。 既定では、pytest フィクスチャは function スコープで動作します。これは、次の 2 つの方法で動作に影響します。

  • テストごとのライフサイクル: フィクスチャの戻り値は、それを使用するすべてのテスト関数に対して再計算され、各テストが最新の状態で動作することを保証します。
  • 各使用後のクリーンアップ: 必要なクリーンアップ操作は、フィクスチャを使用する各テストの後に実行されます。

Pytest では、フィクスチャの範囲をより広く設定して、パフォーマンスとリソースの使用を最適化することもできます。 スコープは、データベースの状態を管理する場合や、確立に時間がかかる複雑な状態のセットアップがある場合に特に便利です。 次の 4 つのスコープを使用できます。

  • function: デフォルトのスコープでは、フィクスチャは各テストごとに1回実行されます。
  • class:フィクスチャはテスト クラスごとに 1 回実行されます。
  • module: モジュールに対して 1 回実行されます。
  • session: テスト セッションごとに 1 回実行されます。 このスコープは、サービスの初期化やデータベース サーバーの起動など、テスト セッション全体にわたって保持する必要があるコストの高い操作に役立ちます。

この場合、 1 回実行 すると、戻り値がキャッシュされます。 そのため、"module" のスコープを持つフィクスチャはテスト モジュールで複数回呼び出すことができますが、戻り値は、それを呼び出した最初のテストの値です。

モジュール スコープを使用した tmp_file() フィクスチャの外観を次に示します。

import pytest
import tempfile

@pytest.fixture(scope="module")
def tmp_file():
    def create():
        temp = tempfile.NamedTemporaryFile(delete=False)
        return temp.name
    return create

クリーンアップ管理

tmp_fileフィクスチャを指定する前のコードでは一時ファイルが作成されますが、テストの完了後にクリーンアップは自動的に処理されません。 一時ファイルが残らないようにするには、pytest の request フィクスチャを使用してクリーンアップ関数を登録します。

自動クリーンアップを含むように tmp_file フィクスチャを変更する方法を次に示します。

import pytest
import tempfile
import os

@pytest.fixture(scope="module")
def tmp_file(request):
    # Create a temporary file that persists beyond the function scope
    temp = tempfile.NamedTemporaryFile(delete=False)

    def create():
        # Returns the path of the temporary file
        return temp.name

    def cleanup():
        # Remove the file after the tests are done
        os.remove(temp.name)

    # Register the cleanup function to be called after the last test in the module
    request.addfinalizer(cleanup)

    return create

request.addfinalizer()を使用し、入れ子になったcleanup()関数を渡すことで、スコープに応じてクリーンアップが呼び出されます。 この場合、スコープは moduleされるため、モジュール pytest 内のすべてのテストでクリーンアップ関数が呼び出されます。

conftest.py の使用

テスト ファイルにフィクスチャを含める代わりに、conftest.py ファイルに保存できます。 conftest.py 内のすべてのフィクスチャは、明示的にインポートしなくても、同じディレクトリ内のテストで自動的に使用できます。

組み込み固定具の探索

Pytest には、テストを効率化するように設計された多くの組み込みフィクスチャがあります。 これらのフィクスチャは、セットアップとクリーンアップを自動的に処理できるため、テスト管理の代わりにテスト ケースの作成に集中できます。

次のような主要な内蔵設備が含まれています:

  • cache: テスト レベルのキャッシュを作成および管理するために使用されます。これは、テスト セッション間のデータの格納に役立ちます。
  • capsys: stderrstdoutの検査をキャプチャして許可し、コンソール出力の検査とテストを簡単に行うことができます。
  • tmpdir: テスト中に作成して使用する必要があるファイルの一時ディレクトリを提供します。
  • monkeypatch: オブジェクト、関数、および os 環境の動作と値を安全に変更する方法を提供します。

テストにおけるモンキーパッチの役割

データベースや外部 API などの外部リソースと緊密に統合されるコードのテストは、依存関係が原因で困難になる場合があります。 モンキー パッチ適用と呼ばれる手法には、テストの実行中にシステムを一時的に変更することが含まれます。これにより、外部システムからの独立が可能になり、テスト中にオペレーティング システム環境の状態と動作を安全に変更できます。

monkeypatchフィクスチャを使用してos.path.exists()関数をオーバーライドする方法の例を次に示します。

import os

def test_os(monkeypatch):
    # Override os.path.exists to always return False
    monkeypatch.setattr('os.path.exists', lambda x: False)
    assert not os.path.exists('/')

または、 setattr() メソッドを使用して、オブジェクトと属性を直接参照することもできます。

def test_os(monkeypatch):
    # Specify the object and attribute to override
    monkeypatch.setattr(os.path, 'exists', lambda x: False)
    assert not os.path.exists('/')

monkeypatchフィクスチャは、属性の設定やメソッドのオーバーライドとは別に、環境変数の設定と削除、ディクショナリ値の変更、システム パスの変更を行うことができます。 monkeypatchフィクスチャは、各テストの後に変更を自動的に元に戻しますが、monkeypatchフィクスチャを使用する場合は注意が必要です。 使用する際に注意すべきいくつかの理由を次に示します。

-コードの明確さとメンテナンス: monkeypatch を過剰に使用したり、複雑な方法で使用したりすると、テストの理解と保守が難しくなる可能性があります。 テスト結果を読むと、コンポーネントの正常な動作とテスト用の変更方法がすぐには明確にならない場合があります。-テストの有効性:モンキー パッチを実施した結果、運用環境とは大きく異なる人工的な条件下で合格するようなテストが行われる場合があります。 これにより、テストがシステムの動作を大幅に変更しすぎたためにテストが通ってしまうことがあり、その結果として誤った安心感が生まれる可能性があります。-実装の詳細への過度の依存: モンキーパッチに依存するテストは、テストしているコードの特定の実装の詳細と密接に結び付いている可能性があります。 これにより、テストが脆弱になり、基になるコードベースに対する軽微な変更でも破壊的な影響を受けやすくなります。-デバッグの複雑さ: monkeypatch を使用するテストのデバッグは、特にパッチによってアプリケーションの動作の基本的な側面が変更される場合に、より複雑になる可能性があります。 テストが失敗する理由を理解するには、テスト中にコンポーネントがどのように変更されているかを詳しく調べる必要がある場合があります。

monkeypatchは、分離された制御されたテスト環境を作成するための強力なツールですが、テスト スイートとアプリケーションの動作に与える影響を明確に理解して慎重に使用する必要があります。