Pythonモック・スタブ完全ガイド – テスト効率化の実践的活用法

 

Pythonでテストを書く際、外部APIやデータベース、時間に依存する処理などをテストするのは困難です。そこで活用されるのがモック(Mock)とスタブ(Stub)です。本記事では、Pythonにおけるモックとスタブの基本から実践的な活用法まで詳しく解説します。

モックとスタブの基本概念

モックとは

モックは実際のオブジェクトの振る舞いを模倣するテスト用のオブジェクトです。メソッドの呼び出し回数や引数を記録し、検証することができます。

スタブとは

スタブは特定の入力に対して予め定義された値を返すシンプルなテスト用オブジェクトです。モックよりも軽量で、単純な代替処理に適しています。

Pythonでのモック・スタブの実装方法

1. unittestモジュールのMockを使用

Pythonの標準ライブラリunittest.mockを使用した基本的なモックの作成方法です。

from unittest.mock import Mock, patch
import unittest

class Calculator:
    def add(self, a, b):
        return a + b

class TestCalculator(unittest.TestCase):
    def test_mock_basic(self):
        # モックオブジェクトの作成
        mock_calc = Mock()
        mock_calc.add.return_value = 10
        
        # テスト実行
        result = mock_calc.add(5, 3)
        
        # 検証
        self.assertEqual(result, 10)
        mock_calc.add.assert_called_once_with(5, 3)

2. patchデコレータを使用したモック

外部依存をモックする際に便利なpatchデコレータの使用例です。

import requests
from unittest.mock import patch

def get_user_data(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()

@patch('requests.get')
def test_get_user_data(mock_get):
    # モックの戻り値を設定
    mock_get.return_value.json.return_value = {"id": 1, "name": "太郎"}
    
    result = get_user_data(1)
    
    assert result["name"] == "太郎"
    mock_get.assert_called_once_with("https://api.example.com/users/1")

3. スタブの実装

シンプルなスタブクラスの実装例です。

class DatabaseStub:
    def __init__(self):
        self.data = {"user1": "太郎", "user2": "花子"}
    
    def get_user(self, user_id):
        return self.data.get(user_id, "Unknown")

def test_with_stub():
    db_stub = DatabaseStub()
    result = db_stub.get_user("user1")
    assert result == "太郎"

実践的な活用シーン

1. 外部API呼び出しのテスト

from unittest.mock import patch, Mock

class WeatherService:
    def get_temperature(self, city):
        # 実際のAPI呼び出し処理
        pass

@patch.object(WeatherService, 'get_temperature')
def test_weather_service(mock_temp):
    mock_temp.return_value = 25.5
    
    service = WeatherService()
    temp = service.get_temperature("Tokyo")
    
    assert temp == 25.5

2. データベース操作のモック

from unittest.mock import MagicMock

class UserRepository:
    def save_user(self, user):
        # データベース保存処理
        pass

def test_user_save():
    mock_repo = MagicMock()
    mock_repo.save_user.return_value = True
    
    result = mock_repo.save_user({"name": "太郎"})
    
    assert result is True
    mock_repo.save_user.assert_called_once()

3. 時間依存処理のテスト

from datetime import datetime
from unittest.mock import patch

def get_current_hour():
    return datetime.now().hour

@patch('datetime.datetime')
def test_current_hour(mock_datetime):
    mock_datetime.now.return_value = datetime(2024, 1, 1, 15, 30)
    
    result = get_current_hour()
    
    assert result == 15

モック・スタブ使用時のベストプラクティス

1. 過度なモックは避ける

必要最小限のモックを使用し、実際のコードに近い状態でテストを実行することが重要です。

2. モックの設定は明確に

モックの戻り値や振る舞いは明確に定義し、テストの意図を分かりやすくしましょう。

3. モックの検証を忘れずに

モックが期待通りに呼び出されたかをassert_called_withなどで検証することが大切です。

よくある落とし穴と対処法

パッチの適用場所を間違える

# 間違い:インポート元ではなく使用場所をパッチ
@patch('mymodule.requests.get')  # ✗

# 正しい:使用される場所をパッチ
@patch('requests.get')  # ✓

モックの設定漏れ

# 設定漏れがあるとAttributeErrorが発生
mock_obj = Mock()
# mock_obj.method.return_value = "value"  # この設定が必要
result = mock_obj.method()  # エラーになる可能性

まとめ

Pythonのモックとスタブを活用することで、外部依存を排除した高速で安定したテストが作成できます。特にunittest.mockモジュールのMockMagicMockpatchを適切に使い分けることで、効率的なテスト開発が可能になります。

テスト駆動開発(TDD)を実践する際にも、モックとスタブは欠かせないツールです。本記事で紹介した手法を参考に、品質の高いPythonアプリケーションの開発を進めてください。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座