Python のモック関数
unittest.mock
または Mock
関数は、Python でテストするためのライブラリであり、テスト対象のシステムのコンポーネントを mock
オブジェクトに置き換え、パーツがどのように使用されたかについてアサーションを行うことができます。
unittest.mock
はコアの Mock
クラスを提供し、テスト スイート全体に多数のスタブを作成する必要をなくします。
プロセスを実行した後、使用されたメソッドまたは属性と、それらが呼び出された引数をアサートできます。 通常の方法で戻り値を指定し、必要な属性を設定することもできます。
さらに、Mock
は、パッチ モジュールとテスト スコープ内のクラス レベル属性を処理できる patch()
デコレータと、一意のオブジェクトを作成するためのヘルパー sentinel
を提供します。
Mock
は unittest
で使用するために作成され、ほとんどのモック フレームワークで使用される record-to-replay
の代わりに action-to-assertion
パターンに基づいています。 以前のバージョンの Python 用の unittest.mock
のバックポートがあります。
Python で Pytest Mock をセットアップしてインストールする
unittest
とは対照的に、pytest
は組み込みの Python パッケージではなく、インストールが必要です。 これは、ターミナルで以下のコマンドを入力することでインストールできます。
pip install pytest
注意として、unittest
のベスト プラクティスは pytest
にも適用されます。
- すべての単体テストを格納するには、
tests/
ディレクトリが必要です。 - ファイル名は常に
tests_
で始まる必要があります。 - 関数名は常に
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 秒かかり、現在のインスタンスによって異なる場合があります。
単体テストは迅速である必要があり、数秒で何百ものテストを実行できる必要があります。 テスト スイートは、5 秒かかる 1つのテストによって遅くなるため、モックを適用すると作業が楽になります。
遅い関数にパッチを当てると、get_os()
関数の動作を 5 秒間待たずに確認できます。
この関数を pytest-mock
でモックしてみましょう。
Pytest-mock
は、mocker
と呼ばれるフィクスチャと、Python の統合されたモッキング コンストラクトの上に優れたインターフェイスを提供します。 モック関数とパッチ関数を呼び出し、それらを引数としてテスト関数に送信することで、モッカーを使用できます。
isWindows
関数が貴重な 5 秒を待たずに True
を返すようにしたい場合は、次のようにパッチを適用できます。
mocker.patch("app.isWindows", return_value=True)
ここでは、app
モジュール内の関数であるため、isWindows
を app.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"
これで、このテストを実行すると、はるかに迅速に終了します。
ご覧のとおり、テストにかかった時間はわずか 0.02 秒でした。そのため、処理速度の遅い関数にパッチを適用し、テスト スイートを高速化することができました。
モックのもう 1つの利点は、モック関数が何かを返すようにでき、エラーを発生させて、それらのシナリオでコードがどのように処理されるかをテストできることです。
ここで、isWindows
が False
を返すケースをテストする場合は、次のテストを記述します。
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"
モッカーに付属するモックとパッチはすべて関数スコープです。つまり、特定の関数でのみ使用できます。 その結果、異なるテストで同じ機能のパッチ間で競合が発生することはありません。
まとめ
モッキングとは、テストしているアプリケーション コンポーネントを、そのコンポーネントのダミー実装であるモックに置き換える方法です。 モックを作成することで、速度の向上、より高速に実行されるテストが非常に有益である、テスト中の望ましくない副作用の回避などの利点を得ることができます。
Nimesha is a Full-stack Software Engineer for more than five years, he loves technology, as technology has the power to solve our many problems within just a minute. He have been contributing to various projects over the last 5+ years and working with almost all the so-called 03 tiers(DB, M-Tier, and Client). Recently, he has started working with DevOps technologies such as Azure administration, Kubernetes, Terraform automation, and Bash scripting as well.