📝
Python Mockを使用したテストの書き方
モック技術は、単体テストや統合テストを行う際に非常に有用です。実際の依存関係を模倣し、予測可能で制御可能な環境でテストを行うことができます。この記事では、モック技術を使用したテストの方法を解説し、具体的なサンプルコードを提供します。
目次
- モック技術とは
- モック技術の利点
- テストケースの概要
- モックを使ったテストの準備
- テストケースの解説
- サンプルコード
1. モック技術とは
モック技術は、テスト中に依存する外部リソース(例えば、データベース、外部API、ファイルシステムなど)を模倣する方法です。モックオブジェクトは、実際のオブジェクトの代わりに使用され、テストが予測可能で制御可能な結果を提供するようにします。
2. モック技術の利点
- 制御可能: テスト環境を完全に制御することができます。
- 高速化: 外部サービスやデータベースへのアクセスを避けることで、テストの実行速度を向上させます。
- 再現性: 同じテストを何度も再現可能にします。
3. テストケースの概要
以下のテストケースでは、ECサイトのAPIを使用して商品を作成、更新、および取得する機能をテストしています。モック技術を使用して外部サービスへの依存を排除し、テストを実行可能にしています。
4. モックを使ったテストの準備
モック技術を使用するために、以下のライブラリを使用します。
-
pytest
: Pythonのテストフレームワーク -
pytest-asyncio
: 非同期コードのテストをサポートするプラグイン -
unittest.mock
: モックオブジェクトを作成するための標準ライブラリ -
mongomock_motor
: 非同期MongoDBモッククライアント
5. テストケースの解説
テストケースでは、以下の手順でテストを行います。
- モックデータベースとモックサービスを準備する。
- モックを使ってテストを実行する。
6. サンプルコード
以下に、モック技術を使用したテストケースの完全なサンプルコードを示します。
サンプルコード
import pytest
import pytest_asyncio
from unittest.mock import patch, AsyncMock
from mongomock_motor import AsyncMongoMockClient
from fastapi.testclient import TestClient
from my_ec_site.main import app
from my_ec_site.schemas.product import ProductReqFactory
from my_ec_site.services.notification_service import NotificationService
client = TestClient(app)
# モックデータベースとモックサービスを準備するフィクスチャ
@pytest_asyncio.fixture()
async def prepare_mock_db():
client = AsyncMongoMockClient()
database = client["test_db"]
await init_beanie_with_models(database)
@pytest_asyncio.fixture
def mock_notification_service():
# NotificationServiceのモックを作成
with patch("my_ec_site.services.notification_service.NotificationService") as mock_notification_service:
mock_instance = mock_notification_service.return_value
# send_notificationメソッドをモックして、常にTrueを返すように設定
mock_instance.send_notification = AsyncMock(return_value=True)
yield mock_instance
@pytest_asyncio.fixture
def override_dependency(mock_notification_service):
# NotificationServiceの依存関係をモックに置き換える
def _get_mock_service():
return mock_notification_service
app.dependency_overrides[NotificationService] = _get_mock_service
yield
# テスト終了後に依存関係の置き換えをクリアする
app.dependency_overrides.clear()
# 商品の更新テスト
@pytest.mark.asyncio
async def test_update_product(prepare_mock_db, mocker):
notification_service_mock = mocker.patch.object(NotificationService, "send_notification")
req = ProductReqFactory.build()
# 商品を作成
create_response = client.post("/products", data=req.model_dump_json())
created_product = create_response.json()
# 更新用のリクエストを生成
update_req = ProductReqFactory.build()
update_req.status = "updated-status"
# 商品を更新
update_response = client.put(f"/products/{created_product['id']}", data=update_req.model_dump_json())
# ステータスコードが200(OK)であることを確認
assert update_response.status_code == 200
updated_product = update_response.json()
# 作成された商品と更新された商品のIDが一致することを確認
assert created_product["id"] == updated_product["id"]
# 更新されたステータスを確認
assert updated_product["status"] == "updated-status"
# 通知サービスが1回だけ呼び出されることを確認
assert notification_service_mock.call_count == 1
# 商品の取得テスト
@pytest.mark.asyncio
async def test_get_product(override_dependency, prepare_mock_db):
req = ProductReqFactory.build()
# 商品を作成
create_response = client.post("/products", data=req.model_dump_json())
created_product = create_response.json()
# 作成された商品をIDで取得
response = client.get(f"/products/{created_product['id']}")
assert response.status_code == 200
fetched_product = response.json()
# 取得した商品と作成された商品のIDが一致することを確認
assert fetched_product["id"] == created_product["id"]
サンプルコードの解説
-
フィクスチャの準備:
-
prepare_mock_db
: モックデータベースを初期化します。 -
mock_notification_service
:NotificationService
のモックを作成します。 -
override_dependency
:NotificationService
の依存関係をモックで置き換えます。
-
-
mock_notification_service
:-
patch
を使用してNotificationService
クラスをモック化します。 -
send_notification
メソッドをAsyncMock
を使用してモックし、常にTrue
を返すように設定します。これにより、通知サービスが実際に動作しなくてもテストを行うことができます。
-
-
override_dependency
:-
app.dependency_overrides
を使用して、NotificationService
の依存関係をモックに置き換えます。これにより、テスト中に実際のサービスではなくモックが使用されるようになります。 - テストが終了したら、
app.dependency_overrides.clear()
を呼び出して、依存関係の置き換えをクリアします。
-
-
テストケース:
-
test_update_product
: 商品の更新機能をテストします。NotificationService
のメソッドが期待通りに呼び出されていることを確認します。 -
test_get_product
: 商品の取得機能をテストします。作成した商品が正しく取得できることを確認します。
-
これらのテストケースは、外部依存をモックすることで、テストが予測可能で制御可能な環境で行えるようにしています。モック技術を使用することで、信頼性の高いテストを効率的に行うことができます。
Discussion