ユニットテストを書くときに便利なunittest.mockライブラリをバリバリ使いこなしたい!
以下の順番で勉強していきます。今回は①をやっていきます。
①Mockオブジェクトのふるまいを指定する(return_value,side_effectを使う)、MagicMockとMockの違いを理解する
②オブジェクトをMockオブジェクトに差し替える
③ ①②で登場しなかった機能を紹介
Mockオブジェクトをつくる
実行したバージョンはPython3.7.3です。
unittest.mockはPython3.3以降は標準ライブラリとして利用可能です。それ以前のバージョンの場合はpipでインストールしてください。
Mockオブジェクトをつくってみる↓
>>> from unittest.mock import Mock
>>> a = Mock()
>>> a
<Mock id='48605424'>
>>> type(a)
<class 'unittest.mock.Mock'>
Mockオブジェクトのふるまいを指定する
オプションを設定することでMockオブジェクトの振る舞いは自由に指定できます。よく使う設定をまとめていきます。
###return_value
return_valueを使うことで、Mockオブジェクトが呼ばれたときの返り値を定義できます。
# return_valueを設定していないときの返り値はMockオブジェクト
>>> b = Mock()
>>> b()
<Mock name='mock()' id='48614416'>
# return_valueを設定すると設定した値が返り値になる
>>> a = Mock()
>>> a.return_value = 'hoge'
>>> a()
'hoge'
# こう書いても同じ
>>> c = Mock(return_value='hoge')
>>> c()
'hoge'
###side_effect
Mockオブジェクトが呼ばれたときに呼ばれる関数や、発生する例外を指定することができます。
return_valueだと1つの値しか設定できませんでしたが、side_effectを使うと条件によって返り値を変えたりできます。
# Mockオブジェクトが呼ばれたときにwhoメソッドが呼ばれるよう設定する
>>> m = Mock()
>>> def who(num):
... if num == 1:
... return 'いちろう'
... if num == 2:
... return 'じろう'
...
>>> m.side_effect = who
>>> m(1)
'いちろう'
>>> m(2)
'じろう'
# Mockオブジェクトが呼ばれたときに発生する例外を設定する
>>> m = Mock()
>>> m.side_effect = Exception('Out!')
>>> m()
Traceback (most recent call last):
# 中略
Exception: Out!
#####return_valueとside_effectを両方指定したらどうなるか?
side_effectが優先されます。side_effectにNoneを指定すると設定がクリアされ、return_valueが適用されます。
>>> def who(num):
... if num == 1:
... return 'いちろう'
... if num == 2:
... return 'じろう'
>>> m = Mock()
>>> m.return_value = 'さぶろう'
>>> m(1)
'さぶろう'
>>> m.side_effect = who
# side_effectが優先される
>>> m(1)
'いちろう'
>>> m(2)
'じろう'
# side_effectをクリアするとreturn_valueが適用される
>>> m.side_effect = None
>>> m(1)
'さぶろう'
>>> m(2)
'さぶろう'
Mockのサブクラス、MagicMock
MagicMockはMockのサブクラスです。Mockとの違いは特殊メソッドをデフォルトで実装している点です。
どういうことか見てみましょう。
>>> from unittest.mock import Mock
>>> from unittest.mock import MagicMock
>>> mock = Mock()
>>> len(mock)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'Mock' has no len()
# mockは__len__()が定義されていないので、len()をコールできない。
>>> mock.__len__()
...省略
AttributeError: __len__
# __len__()を定義してやるとコールできるようになる。
>>> mock.__len__ = lambda x:1
>>> mock.__len__()
1
>>> len(mock)
1
# 同じことをMagicMockオブジェクトでやってみる
>>> magic = MagicMock()
# __len__()が定義されているので、len()をコールできる。
>>> magic.__len__()
0
>>> len(magic)
0
# もちろん書き換えもできる
>>> magic.__len__ = lambda x:1
>>> len(magic)
1
このようにMagicMockオブジェクトではほとんどの特殊メソッドをデフォルトで実装してくれています。
自分で定義しなくてもいいのでMockよりMagicMockのほうが使い勝手がよさそうですね!
ただし、デフォルト実装の内容を理解してうえで使う必要があります。上記例の__len__()
のようにデフォルト実装だと常に結果は0になります。__bool__()
だと常に結果はTrue
になります。
※デフォルト実装している特殊メソッドとデフォルト実装の内容の詳細は公式リファレンスをご確認ください。 https://docs.python.org/ja/3/library/unittest.mock.html#unittest.mock.MagicMock