
スポンサーリンク
Pythonには、標準ライブラリとしてunittest
というテストフレームワークが用意されています。また、unittest.mock
モジュールを使うことで、外部依存を排除した単体テストが容易に作成できます。本記事では、unittest
とmock
の基本的な使い方や単体テストコードの実行方法を解説します。
1. unittestの基本
unittest
はPythonの標準テストフレームワークであり、テストケースの作成、実行、アサーションをサポートしています。
テストケースの作成方法
テストコードは以下のtest_add(self)
のようにtest_
から始まる関数名で作成します。
import unittest
# テスト対象の関数def add(a, b): return a + b
class TestMathOperations(unittest.TestCase): def test_add(self): self.assertEqual(add(2, 3), 5) self.assertEqual(add(-1, 1), 0) self.assertEqual(add(0, 0), 0)
if __name__ == "__main__": unittest.main()
assertEqual
でaddの結果と期待値が同一であることを確認しています。
テストケースの実行方法
作成したテストコードを実行するには、以下のコマンドを使用します。
python -m unittest -v unittest-ex01
実行結果は、以下のように表示されます。
test_add (test.TestMathOperations.test_add) ... ok
----------------------------------------------------------------------Ran 1 test in 0.002s
OK
特定のクラス・メソッドを実行
特定のテストクラスやテストメソッドを実行したい場合は、以下のように指定します。
python -m unittest -v unittest-ex01.TestMathOperationspython -m unittest -v unittest-ex01.TestMathOperations.test_add
2. unittest.mockの使い方
unittest.mock
を使うと、関数やクラスをモック(偽物のオブジェクト)に置き換えてテストを実施できます。
mock.patch
は、特定のモジュールやクラスのメソッドを一時的にモックに置き換えるためのデコレーターまたはコンテキストマネージャーです。
モックには例えば以下のような設定が可能です。
return_value
: モックが呼ばれた際に返す値を指定side_effect
: 呼び出しごとに異なる動作を指定(関数を渡すことも可能)
外部APIの呼び出しをモックする
以下に実装例を示します。
from unittest import TestCasefrom unittest.mock import patchimport requests
# テスト対象の関数def fetch_data(url): response = requests.get(url) return response.json()
class TestFetchData(TestCase): @patch("requests.get") def test_fetch_data(self, mock_get): mock_get.return_value.json.return_value = {"message": "Hello, world!"} result = fetch_data("http://example.com") self.assertEqual(result, {"message": "Hello, world!"})
@patch
でrequests.getをモック化し、戻り値(return_value)に{"message": "Hello, world!"}
を指定しています。
※実行にはrequestsモジュールが必要です。
デコレーターとしての使用
以下のように@patch.object
を指定し、デコレーター内にreturn_value
を指定することも可能です。
import unittestfrom unittest.mock import patch
class MyClass: def method(self): return "real value"
class TestMyClass(unittest.TestCase): @patch.object(MyClass, "method", return_value="mocked value") def test_method(self, mock_method): obj = MyClass() self.assertEqual(obj.method(), "mocked value")
if __name__ == "__main__": unittest.main()
@patch.object
デコレーター内でMyClassのmethodの戻り値をmocked value
に指定し、assertEqual
でmocked value
の期待値比較を行います。
コンテキストマネージャーとしての使用
withブロックでmockを指定することもできます。以下に実装例を示します。
import unittestfrom unittest.mock import patchimport requests
class TestMyClass(unittest.TestCase): def test_method(self): with patch("requests.get") as mock_get: mock_get.return_value.json.return_value = {"status": "ok"} response = requests.get("http://example.com") assert response.json() == {"status": "ok"}
if __name__ == "__main__": unittest.main()
requests.get
の戻り値のjsonの戻り値に{"status": "ok"}
を指定、assertで取得した値を比較するテストコードです。
クラスのフィールドをモックする
クラスのインスタンス変数(フィールド)をモックすることで、特定の状態をシミュレートできます。
import unittestfrom unittest.mock import patch
class Config: def __init__(self): self.api_key = "real_api_key"
class Service: def __init__(self, config): self.config = config
def get_api_key(self): return self.config.api_key
class TestService(unittest.TestCase): def test_get_api_key(self): config = Config() with patch.object(config, "api_key", new="mocked_api_key"): # インスタンス変数をモック化 service = Service(config) self.assertEqual(service.get_api_key(), "mocked_api_key")
if __name__ == "__main__": unittest.main()
上記patch.object
でConfingクラスのapi_key
をmocked_api_key
でモック化しています。
呼び出しごとに異なる値を返す
side_effect
を利用すると呼び出しごとに異なる結果を出力することができます。
from unittest import TestCasefrom unittest.mock import patch
def fetch_data(): import requests response = requests.get("http://example.com") return response.json()
class TestFetchData(TestCase): @patch("requests.get") def test_fetch_data_multiple_values(self, mock_get): mock_get.return_value.json.side_effect = [ {"status": "ok"}, {"status": "error"} ]
self.assertEqual(fetch_data(), {"status": "ok"}) # 1回目の呼び出し self.assertEqual(fetch_data(), {"status": "error"}) # 2回目の呼び出し
上記例ではfetch_data()
を1回目と2回目で異なるレスポンスを返す挙動になります。
例外を発生させる
side_effectを利用することで、特定の例外を発生させることもできます。
from unittest import TestCasefrom unittest.mock import patch
def fetch_data(): import requests response = requests.get("http://example.com") return response.json()
class TestFetchData(TestCase): @patch("requests.get") def test_fetch_data_raises_exception(self, mock_get): mock_get.side_effect = Exception("Network error")
with self.assertRaises(Exception) as context: fetch_data()
self.assertEqual(str(context.exception), "Network error")
上記例ではfetch_data
の結果にNetwork error
を発生させています。
まとめ
unittest
を使って基本的なテストケースを作成できる。unittest.mock
を利用すると、外部依存を排除したテストが可能になる。patch
を活用することで、特定の関数やメソッドをモックできる。- クラスのフィールドを
patch.object
でモックすることで、環境設定や状態を変更可能。 side_effect
を利用すると、呼び出しごとに異なる値を返したり、例外を発生させることができて、より柔軟なテストが可能になる。return_value
を利用すると、指定した固定値を返すことができる。
unittest
とmock
を活用して、効率的なテストを実装しましょう!
参考
unittest --- ユニットテストフレームワーク
unittest.mock --- モックオブジェクトライブラリ
以上で本記事の解説を終わります。
よいITライフを!