PYTHON MEBY

Pythonでモックを使う(unittest.mock, pytest-mock)

この記事では、Pythonでモックオブジェクトを使ってテストを書きやすくする方法を説明します。unittest.mockとpytest-mockの使い方を例を通して学びます。

目次

unittest.mockによるモック

unittest.mockモジュールは、テスト対象のコードを分離し、依存関係を置き換えるためのモックオブジェクトを作成するのに役立ちます。

import unittest
from unittest.mock import patch

class MyClass:
    def my_method(self, arg):
        return arg * 2

class TestMyClass(unittest.TestCase):
    @patch('__main__.MyClass.my_method')
    def test_my_method(self, mock_my_method):
        mock_my_method.return_value = 10
        my_instance = MyClass()
        result = my_instance.my_method(5)
        self.assertEqual(result, 10)

この例では、MyClass.my_methodをモックし、return_value属性で戻り値を制御しています。

テストの書き方

モックを使うことで、外部システムとの連携や、ランダムな要素を含む関数などを簡単にテストできます。

import unittest
from unittest.mock import Mock

class ExternalAPI:
    def call_api(self):
        # 実際には外部APIを呼び出す処理がある
        return {"data": "from api"}

class MySystem:
    def __init__(self, api):
        self.api = api

    def process_data(self):
        data = self.api.call_api()
        return data["data"]

class TestMySystem(unittest.TestCase):
    def test_process_data(self):
        mock_api = Mock()
        mock_api.call_api.return_value = {"data": "mocked data"}
        system = MySystem(mock_api)
        result = system.process_data()
        self.assertEqual(result, "mocked data")

パッチを使ったモッキング

patchデコレータを使うと、関数やメソッドをモックオブジェクトに置き換えることができます。

import unittest
from unittest.mock import patch

import os

@patch('os.getcwd')
def test_getcwd(mock_getcwd):
    mock_getcwd.return_value = '/tmp'
    current_dir = os.getcwd()
    assert current_dir == '/tmp'

pytest-mockによるモック

pytest-mockを使うと、pytestで簡単にモックオブジェクトを作成できます。

import pytest

class MyClass:
    def my_method(self, arg):
        return arg * 2

def test_my_method(mocker):
    mock_my_method = mocker.patch('__main__.MyClass.my_method')
    mock_my_method.return_value = 10
    my_instance = MyClass()
    result = my_instance.my_method(5)
    assert result == 10

より高度なモックの使い方

side_effect属性を使うと、モックオブジェクトの呼び出しごとに異なる値を返すことができます。

from unittest.mock import Mock

mock = Mock(side_effect=[1, 2, 3])
print(mock())  # 1
print(mock())  # 2
print(mock())  # 3
print(mock())  # 3

関連記事