HOWTO · Python

Python のモック関数

この記事では、組み込みの Python パッケージではない pytest-mock を使用して、Python の unittest ライブラリで関数をモックし、モックでのテストを成功させる方法について説明します。

このページの内容

unittest.mock または Mock 関数は、Python でテストするためのライブラリであり、テスト対象のシステムのコンポーネントを mock オブジェクトに置き換え、パーツがどのように使用されたかについてアサーションを行うことができます。

unittest.mock はコアの Mock クラスを提供し、テスト スイート全体に多数のスタブを作成する必要をなくします。

プロセスを実行した後、使用されたメソッドまたは属性と、それらが呼び出された引数をアサートできます。 通常の方法で戻り値を指定し、必要な属性を設定することもできます。

さらに、Mock は、パッチ モジュールとテスト スコープ内のクラス レベル属性を処理できる patch() デコレータと、一意のオブジェクトを作成するためのヘルパー sentinel を提供します。

Mockunittest で使用するために作成され、ほとんどのモック フレームワークで使用される record-to-replay の代わりに action-to-assertion パターンに基づいています。 以前のバージョンの Python 用の unittest.mock のバックポートがあります。

Python で Pytest Mock をセットアップしてインストールする

unittest とは対照的に、pytest は組み込みの Python パッケージではなく、インストールが必要です。 これは、ターミナルで以下のコマンドを入力することでインストールできます。

pip install pytest

注意として、unittest のベスト プラクティスは pytest にも適用されます。

  1. すべての単体テストを格納するには、tests/ ディレクトリが必要です。
  2. ファイル名は常に tests_ で始まる必要があります。
  3. 関数名は常に the test で始まる必要があります。

実行された単体テストをチェッカーが見つけられるように、命名基準を遵守する必要があります。

使用する前に、pytest-mock をインストールする必要があります。 pip を使用してインストールする方法は次のとおりです。

pip install pytest-mock

これは Pytest のプラグインです。 したがって、まだ行っていない場合は、Pytest がインストールされます。

Python で関数をモックする

まず、単純な関数 get_os() を作成して、オペレーティング システムとして Windows と Linux のどちらを使用しているかを知らせます。

次のように、app.py という名前のファイルを作成します。

from time import sleep


def isWindows():
    # This sleep can be some complex operation
    sleep(5)
    return True


def get_os():
    return "Windows is the current OS" if isWindows() else "Linux is the current OS"

この関数は、isWindows 関数を使用して、現在のオペレーティング システムが Windows かどうかを判断します。 このisWindows関数が非常に複雑で、実行に数秒かかると仮定すると、ここでは、呼び出されるたびにプログラムを 5 秒間スリープ状態にすることで、この遅い関数をシミュレートできます。

以下は、get_os() 関数の pytest になります。

test_app.py という名前のファイルを pytest に作成します。

from app import get_os


def test_get_os():
    assert get_os() == "Windows is the current OS"

get_os() が遅い関数 isWindows を呼び出すため、テストの進行が遅くなります。 これは、pytest を実行した以下の出力で確認できます。

5.02 秒かかり、現在のインスタンスによって異なる場合があります。

モック関数 python - 出力 1

単体テストは迅速である必要があり、数秒で何百ものテストを実行できる必要があります。 テスト スイートは、5 秒かかる 1つのテストによって遅くなるため、モックを適用すると作業が楽になります。

遅い関数にパッチを当てると、get_os() 関数の動作を 5 秒間待たずに確認できます。

この関数を pytest-mock でモックしてみましょう。

Pytest-mock は、mocker と呼ばれるフィクスチャと、Python の統合されたモッキング コンストラクトの上に優れたインターフェイスを提供します。 モック関数とパッチ関数を呼び出し、それらを引数としてテスト関数に送信することで、モッカーを使用できます。

isWindows 関数が貴重な 5 秒を待たずに True を返すようにしたい場合は、次のようにパッチを適用できます。

mocker.patch("app.isWindows", return_value=True)

ここでは、app モジュール内の関数であるため、isWindowsapp.isWindows として参照する必要があります。 isWindows のみにパッチを適用すると、存在しない test_app ファイル内の isWindows という関数にパッチを適用しようとします。

形式は常に <module_name>.<function_name> であり、正しくモックする方法を知ることが不可欠です。

以下は、パッチ後に変更されたテスト関数について説明しています。

from app import get_os

# using'mocker' fixture provided by pytest-mock


def test_get_os(mocker):
    # mocking the slow-going function and returning True always
    mocker.patch("app.isWindows", return_value=True)
    assert get_os() == "Windows is the current OS"

これで、このテストを実行すると、はるかに迅速に終了します。

モック関数 python - 出力 2

ご覧のとおり、テストにかかった時間はわずか 0.02 秒でした。そのため、処理速度の遅い関数にパッチを適用し、テスト スイートを高速化することができました。

モックのもう 1つの利点は、モック関数が何かを返すようにでき、エラーを発生させて、それらのシナリオでコードがどのように処理されるかをテストできることです。

ここで、isWindowsFalse を返すケースをテストする場合は、次のテストを記述します。

from app import get_os


def test_os_isLinux(mocker):
    mocker.patch(
        "app.isWindows", return_value=False
    )  # set the return value to be False
    assert get_os() == "Linux is the current OS"

モッカーに付属するモックとパッチはすべて関数スコープです。つまり、特定の関数でのみ使用できます。 その結果、異なるテストで同じ機能のパッチ間で競合が発生することはありません。

まとめ

モッキングとは、テストしているアプリケーション コンポーネントを、そのコンポーネントのダミー実装であるモックに置き換える方法です。 モックを作成することで、速度の向上、より高速に実行されるテストが非常に有益である、テスト中の望ましくない副作用の回避などの利点を得ることができます。