環境
- macOS
- Python 3.9.4
※IMO=In my opinion(コメントがあればぜひお願いします)
IMO: ざっくり整理
- モックを呼び出したときに(毎回同じ)返り値を返してほしい場合は
return_value
属性を設定する - モックを呼び出すたびに異なる返り値を返してほしい場合は
side_effect
属性にイテラブルを渡す
実行例
>>> from unittest.mock import Mock
>>> m1 = Mock()
>>> m1.return_value = 108
>>> m1()
108
>>> m1()
108
>>> m2 = Mock()
>>> m2.side_effect = [1, 2, 3]
>>> m2()
1
>>> m2()
2
>>> m2()
3
>>> # m2() # raise StopIteration
利用シーンのイメージ
IMO:モックのreturn_value
やside_effect
は、patch
でモジュールに当てたモックについてよく設定します。
ref: https://docs.python.org/ja/3/library/unittest.mock.html#unittest.mock.patch
return_value
import random
from unittest.mock import patch
with patch("random.choice") as mock_choice:
mock_choice.return_value = 2 # withブロックの中では2しか返らなくなります
for i in range(5):
print(random.choice([1, 2, 3, 4, 5]))
上のスクリプトを実行したときのコマンドライン
2
2
2
2
2
side_effect
import random
from unittest.mock import patch
with patch("random.choice") as mock_choice:
mock_choice.side_effect = (1, 1, 2, 3, 5)
for i in range(5):
print(random.choice([1, 2, 3, 4, 5]))
上のスクリプトを実行したときのコマンドライン
1
1
2
3
5
例えばfor
文で繰り返す処理の中のモックのside_effect
を使って、for
文が回ったあとの値を設定できます。
補足事項
モック生成時にも設定可能
>>> m1 = Mock(return_value = 108)
>>> m2 = Mock(side_effect = [1, 2, 3])
ref: https://docs.python.org/ja/3/library/unittest.mock.html#unittest.mock.Mock.return_value
side_effect
は関数や例外も設定できる
このモックが呼ばれた際に呼び出される関数、イテラブル、もしくは発生させる例外 (クラスまたはインスタンス) を設定できます。
イテラブルの設定は、1つの使い方に過ぎません。
モックを呼び出した返り値はreturn_value属性と一致
>>> from unittest.mock import Mock
>>> m = Mock()
>>> m() # doctest: +ELLIPSIS
<Mock name='mock()' id=...>
>>> m.return_value # doctest: +ELLIPSIS
<Mock name='mock()' id=...>
>>> m() is m.return_value
True
is
がTrue
(idが同じ)なので、同一のオブジェクトです。
IMO:使い分け方針
- モックを呼び出したときの返り値が具体的でなくてもよい場合、
m.return_value
を使う -
具体的な返り値が必要な場合は
m.return_value = ...
と設定
参考資料
- 「unittest.mockを使ってテストを書こう」(PyCon JP 2020)
- 私がモックに慣れる際にとても助けられました:まだmockで消耗してるの?mockを理解するための3つのポイント
- 公式ドキュメント https://docs.python.org/ja/3/library/unittest.mock.html