この記事について
Python の unittest.mock
に関するチートシート。
※あくまで自分用のチートシートなので、テストコードはかなり簡潔に書いています。
公式のドキュメントはこちら
→ https://docs.python.org/ja/3/library/unittest.mock.html
unittest.mock.MagicMock
特定のクラスなどをモックしたい場合に利用。
# これは実装側
class Foo:
# クラスのプロパティ
is_foo = True
# クラスのメソッド
def do_something():
# ...
# これはモック定義
import pytest
from unittest.mock import MagicMock, patch
from path.to.foo import Foo
@pytest.fixture()
def mock_foo():
foo_mock = MagicMock(spec=Foo)
foo_mock.do_something = MagicMock(return_value=123)
with patch('path.to.foo.Foo', return_value=foo_mock):
# 各テストケースはこの with の中で実行される
yield foo_mock # テストケースからモックしたインスタンスを参照するため、yeild に foo_mock を渡す
# これはテストファイル
class TestFoo:
def test(self, mock_foo):
# Fooクラスがモックされた状態でのテストとなる
assert mock_foo.do_something.call_count == 1
unittest.mock.patch
クラスの特定のメソッドなどをモックしたい場合に利用。
from unittest.mock import patch
from path.to.foo import Foo
class TestFoo:
# patch は何度でも指定できる
# patch ではなく patch.object を使うことで、参照元にコードジャンプしやすくなる
@patch.object(Foo, 'do_something')
@patch.object(Foo, 'check_if_ok')
def test(
# 引数の順序は、デコレータで指定した順序の逆になる(Pythonの言語仕様)
mock_check_if_ok,
mock_do_something,
):
# 例外を投げさせる
mock_check_if_ok.side_effect = Exception('NG!')
# 返り値を設定する
mock_do_something.return_value = True
# 1度も実行されていない
assert mock_do_something.assert_not_called()
mock_do_something(1,2,3)
# 実行された
assert mock_do_something.called
# 少なくとも1度実行された
assert mock_do_something.assert_called()
# 1度だけ実行された
assert mock_do_something.assert_called_once()
# 1度だけ実行された(引数指定)
assert mock_do_something.assert_called_once_with(1,2,3)
# 最後に実行された際の引数
args = mock_do_something.call_args.args
assert args[0] == 1
mock_do_something(key='abc', value=100)
# N回実行された
assert mock_do_something.call_count = 2
# 最後に実行された際のキーワード引数
kargs = mock_do_something.call_args.kargs
assert kargs['key'] == 'abc'
# 今までに以下のように実行されたことがある
assert mock_do_something.assert_any_call(1,2,3)
# これまでの実行された引数
assert mock_do_something.call_args_list == ...
以下、個人的な所感。
- 自前で MagicMock() するとモックを元に戻す手間が発生するので、ちょっとしたモックでは patch デコレータを使うのが良さそう
- assert_called() よりは called の方がシンプルで良い
- call_args には最後に実行された引数が入っている、と覚えておく
- assert_any_call() は便利だけど、名称に any が入っているのでちょっと拒否感がある
- assert_has_calls() は使うことはあまり無さそう… 実装を変えたらすぐにテストが壊れそう any_order=True を指定しても良いけど
- call オブジェクトとか気にしたくないので、それを意識させないテストコードにするのが良さそう