Help us understand the problem. What is going on with this article?

Python めざせモックマスター①(Mockオブジェクトのふるまいを指定する)

More than 1 year has passed since last update.

ユニットテストを書くときに便利な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

kanae_y
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした