26.5. unittest.mock
— モックオブジェクトライブラリ¶
バージョン 3.3 で追加.
ソースコード: Lib/unittest/mock.py
unittest.mock
はPython におけるソフトウェアテストのためのライブラリです。テスト中のシステムの一部をモックオブジェクトで置き換え、それらがどのように使われるかをアサートすることができます。
unittest.mock
はコア Mock
クラスを提供しており、それによってテストスイート内でたくさんのスタブを作成しなくてすみます 。アクションの実行後、メソッドや属性の使用や実引数についてアサートできます。また通常の方法で戻り値を明記したり、必要な属性を設定することもできます。
加えて、 mock はテストのスコープ内にあるモジュールやクラスの属性を変更する patch()
デコレータを提供します。さらに、ユニークなオブジェクトの作成には sentinel
が利用できます。 Mock
や MagicMock
、 patch()
の利用例は quick guide を参照してください。
Mock はとても使いやすく、 unittest
で利用するために設計されています。Mock は多くのモックフレームワークで使われる ‘record -> replay’ パターンの代わりに、 ‘action -> assertion’ パターンに基づいています。
以前の Python バージョン向けにバックポートされた unittest.mock
があり、 mock on PyPI として利用可能です。
26.5.1. クイックガイド¶
Mock
および MagicMock
オブジェクトはアクセスしたすべての属性とメソッドを作成し、どのように使用されたかについての詳細な情報を格納します。戻り値を指定したり利用できる属性を制限するために Mock
および MagicMock
を設定でき、どのよう使用されたかについてアサートできます:
>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')
side_effect
によって、モック呼び出し時の例外発生などの副作用を実行できます:
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
... return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)
モックには多くの設定法や挙動の制御法があります。例えば spec 引数によって別のオブジェクトからの仕様を受け取るよう設定できます。 spec にないモックの属性やメソッドにアクセスを試みた場合、 AttributeError
で失敗します。
patch()
デコレータ / コンテキストマネージャーによってテスト対象のモジュール内のクラスやオブジェクトを簡単にモックできます。指定したオブジェクトはテスト中はモック (または別のオブジェクト) に置換され、テスト終了時に復元されます:
>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
... module.ClassName1()
... module.ClassName2()
... assert MockClass1 is module.ClassName1
... assert MockClass2 is module.ClassName2
... assert MockClass1.called
... assert MockClass2.called
...
>>> test()
注釈
patch デコレータをネストした場合、モックは適用されるときと同じ順番 (デコレータを適用するときの通常の python の順番) でデコレートされた関数に渡されます。 つまり下から順に適用されるため、上の例では module.ClassName1
のモックが最初に渡されます。
patch()
では探索される名前空間内のオブジェクトにパッチをあてることが重要です。通常は単純ですが、クイックガイドには where-to-patch を読んでください。
patch()
デコレータと同様に with 文でコンテキストマネージャーとして使用できます:
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
... thing = ProductionClass()
... thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)
また、 patch.dict()
を使うと、スコープ内だけで辞書に値を設定し、テスト終了時には元の状態に復元されます:
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
mock は Python の マジックメソッド のモックをサポートしています。 マジックメソッドのもっとも簡単な利用法は MagicMock
クラスと使うことです。以下のように利用します:
>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()
mock によってマジックメソッドに関数 (あるいは他の Mock インスタンス) を割り当てることができ、それらは適切に呼び出されます。
MagicMock
クラスは、すべてのマジックメソッドがあらかじめ作成されている点を除けば MagicMock
クラスと一緒です (まあ、とにかく便利ってこと)。
以下は通常の Mock クラスでマジックメソッドを利用する例です:
>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'
テスト内のモックオブジェクトが置換するオブジェクトと同じ API を持つことを保証するには、 auto-speccing を使うことができます。パッチをあてる autospec 引数、または create_autospec()
関数を通じて auto-speccing は行われます。
auto-speccing は置換するオブジェクトと同じ属性とメソッドを持つモックオブジェクトを作成し、すべての関数および (コンストラクタを含む) メソッドは本物のオブジェクトと同じ呼び出しシグネチャを持ちます。
誤って使用された場合、モックは製品コードと同じように失敗されることが保証されています:
>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
... pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
...
TypeError: <lambda>() takes exactly 3 arguments (1 given)
create_autospec()
はクラスにおいても利用でき、 __init__
メソッドのシグニチャをコピーします。また、呼び出し可能オブジェクトについても __call__
メソッドのシグニチャをコピーします。
26.5.2. Mock クラス¶
Mock
は、コードにおけるスタブの使用やテストダブルを置き換えるための柔軟なモックオブジェクトです。モックは呼び出し可能で、属性にアクセスした場合それを新たなモックとして作成します [1]。同じ属性にアクセスした場合は常に同じモックを返します。
モックはどのように使われたかを記録するので、コードがモックに行うことについてアサートできます。
MagicMock
は Mock
のサブクラスで、すべてのマジックメソッドが事前に作成され、利用できます。また、呼び出し不可能なモックを作成する場合には、呼び出し不能な変種の NonCallableMock
や NonCallableMagicMock
があります。
patch()
デコレータによって特定のモジュール内のクラスを Mock
オブジェクトで一時的に置換することが簡単にできます。デフォルトでは patch()
は MagicMock
を作成します。 patch()
に渡す new_callable 引数によって、別の Mock
クラスを指定できます。
-
class
unittest.mock.
Mock
(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶ 新しい
Mock
オブジェクトを作成します。Mock
はモックオブジェクトの挙動を指定するオプション引数をいくつか取ります:spec: モックオブジェクトの仕様として働く文字列のリストもしくは存在するオブジェクト (クラスもしくはインスタンス) を指定します。オブジェクトを渡した場合には、 dir 関数によって文字列のリストが生成されます (サポートされない特殊属性や特殊メソッドは除く) 。このリストにない属性にアクセスした際には
AttributeError
が発生します。spec が (文字列のリストではなく) オブジェクトの場合、
__class__
はスペックオブジェクトのクラスを返します。これによってモックがisinstance()
テストに通るようになります。spec_set: より厳しい spec です。こちらを利用した場合、 spec_set に渡されたオブジェクトに存在しない属性に対し 設定 や取得をしようとした際に
AttributeError
が発生します。side_effect: モックが呼び出された際に呼び出される関数を指定します。
side_effect
属性を参考にしてください。例外を発生させたり、動的に戻り値を変更する場合に便利です。関数には、モックと同じ引数が渡され、DEFAULT
を返さない限りはこの関数の戻り値が使われます。一方で、 side_effect には、例外クラスやインスタンスを指定できます。この場合は、モックが呼び出された際に指定された例外を発生します。
もし、 side_effect にイテレート可能オブジェクトを指定した場合には、モックの呼び出しごとに順に値を返します。
side_effect に
None
を指定した場合には、設定がクリアされます。return_value: モックが呼び出された際に返す値です。デフォルトでは (最初にアクセスされた際に生成される) 新しい Mock を返します。
return_value
を参照してください。unsafe: デフォルトでは、何らかの属性が assert または assret で始まると
AttributeError
が上がります。unsafe=True
を渡すと、これらの属性へのアクセスが許可されます。バージョン 3.5 で追加.
wraps: ラップするモックオブジェクトを指定します。 もし wraps に
None
以外を指定した場合には、モックの呼び出し時に指定したオブジェクトを呼び出します (戻り値は実際の結果を返します)。 属性へのアクセスは、ラップされたオブジェクトと対応するモックを返します (すなわち、存在しない属性にアクセスしようとした場合はAttributeError
が発生します)。return_value が設定されている場合には、ラップ対象は呼び出されず、 return_value が返されます。
name: もし、モックが name を持つ場合には、モックの repr として使われます。デバッグの際に役立つでしょう。この値は、子のモックにも伝播します。
モックは、任意のキーワード引数を与えることができます。これらはモックの生成後、属性の設定に使われます。詳細は
configure_mock()
を参照してください。-
assert_called_with
(*args, **kwargs)¶ このメソッドは呼び出しが特定の方法で行われたことをアサートするのに便利な方法です:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
-
assert_called_once_with
(*args, **kwargs)¶ Assert that the mock was called exactly once and that that call was with the specified arguments.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
-
assert_any_call
(*args, **kwargs)¶ モックが特定の引数で呼び出されたことがあるのをアサートします。
The assert passes if the mock has ever been called, unlike
assert_called_with()
andassert_called_once_with()
that only pass if the call is the most recent one, and in the case ofassert_called_once_with()
it must also be the only call.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
-
assert_has_calls
(calls, any_order=False)¶ モックが特定の呼び出しで呼ばれたことをアサートします。呼び出しでは
mock_calls
のリストがチェックされます。any_order が false の場合(デフォルト)、呼び出しは連続していなければなりません。指定された呼び出しの前、あるいは呼び出しの後に余分な呼び出しがある場合があります。
any_order が true の場合、呼び出しは任意の順番でも構いませんが、それらがすべて
mock_calls
に現われなければなりません。>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
-
assert_not_called
()¶ モックが呼ばれないことをアサートします。
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times.
バージョン 3.5 で追加.
-
reset_mock
()¶ モックオブジェクトのすべての呼び出し属性をリセットします:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called False
同じオブジェクトを再利用して一連のアサートを行う場合に便利です。
reset_mock()
は戻り値や、通常の代入で設定したside_effect
やあらゆる子属性をリセット しない ことに注意してください。子のモックや戻り値のモックは (存在すれば) 同様にリセットされます。
-
mock_add_spec
(spec, spec_set=False)¶ モックに仕様を追加します。 spec にはオブジェクトもしくは文字列のリストを指定してください。 spec で設定した属性は、モックの属性としてのみアクセスできます。
spec_set が真なら、 spec 以外の属性は設定できません。
-
attach_mock
(mock, attribute)¶ 属性として mock を設定して、その名前と親を入れ替えます。設定されたモックの呼び出しは、
method_calls
やmock_calls
属性に記録されます。
-
configure_mock
(**kwargs)¶ モックの属性をキーワード引数で設定します。
属性に加え、子の戻り値や副作用もドット表記を用いて設定でき、辞書はメソッドの呼び出し時にアンパックされます:
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
コンストラクタの呼び出しでも同様に行うことができます:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock = Mock(some_attribute='eggs', **attrs) >>> mock.some_attribute 'eggs' >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
configure_mock()
は、モック生成後のコンフィギュレーションを容易に行うために存在します。
-
__dir__
()¶ Mock
オブジェクトは、有用な結果を得るためにdir(some_mock)
の結果を制限します。 spec を設定したモックに対しては、許可された属性のみを含みます。このフィルタが何をしていて、どのように停止させるかは、
FILTER_DIR
を参照してください。
-
_get_child_mock
(**kw)¶ 子のモックを作成し、その値を返すようにしてください。デフォルトでは親と同じタイプで作成されます。サブクラスで子モックの作成される方法をカスタマイズしたい場合には、このメソッドをオーバーライドします。
呼び出し不可能なモックに対しては、(カスタムのサブクラスではなく) 呼び出し可能なモックが使われます。
-
called
¶ このモックが呼び出されたかどうかを表します:
>>> mock = Mock(return_value=None) >>> mock.called False >>> mock() >>> mock.called True
-
call_count
¶ このモックオブジェクトが呼び出された回数を返します:
>>> mock = Mock(return_value=None) >>> mock.call_count 0 >>> mock() >>> mock() >>> mock.call_count 2
-
return_value
¶ モックが呼び出された際に返す値を設定します:
>>> mock = Mock() >>> mock.return_value = 'fish' >>> mock() 'fish'
デフォルトの戻り値はモックオブジェクトです。通常の方法で設定することもできます:
>>> mock = Mock() >>> mock.return_value.attribute = sentinel.Attribute >>> mock.return_value() <Mock name='mock()()' id='...'> >>> mock.return_value.assert_called_with()
return_value
は生成時にも設定可能です:>>> mock = Mock(return_value=3) >>> mock.return_value 3 >>> mock() 3
-
side_effect
¶ このモックが呼ばれた際に呼び出される関数、イテラブル、もしくは発生させる例外 (クラスまたはインスタンス) を設定できます。
関数を渡した場合はモックと同じ引数で呼び出され、
DEFAULT
を返さない限りはその関数の戻り値が返されます。関数がDEFAULT
を返した場合は (return_value
によって) モックの通常の値を返します。iterable が渡された場合、その値はイテレータを取り出すために使用されます。イテレータは毎回の呼び出しにおいて値を yield しなければなりません。この値は、送出される例外インスタンスか、呼び出しからモックに返される値のいずれかです (
DEFAULT
の処理は関数の場合と同一です)。以下はモックが (API による例外の扱いをテストするために) 例外を発生させる例です:
>>> mock = Mock() >>> mock.side_effect = Exception('Boom!') >>> mock() Traceback (most recent call last): ... Exception: Boom!
side_effect
を使用して連続的に値を返します:>>> mock = Mock() >>> mock.side_effect = [3, 2, 1] >>> mock(), mock(), mock() (3, 2, 1)
呼び出し可能オブジェクトを使います:
>>> mock = Mock(return_value=3) >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> mock.side_effect = side_effect >>> mock() 3
side_effect
は生成時にも設定可能です。呼び出し時の値に 1 を加えて返す例を以下に示します:>>> side_effect = lambda value: value + 1 >>> mock = Mock(side_effect=side_effect) >>> mock(3) 4 >>> mock(-8) -7
side_effect
にNone
を設定した場合はクリアされます:>>> m = Mock(side_effect=KeyError, return_value=3) >>> m() Traceback (most recent call last): ... KeyError >>> m.side_effect = None >>> m() 3
-
call_args
¶ (もし呼び出されたことがなければ)
None
、それ以外であれば最後に呼び出された時の引数を返します。引数はタプルの形式で表されます: 最初の要素はモックが呼び出された際の順序付きの引数 (もしくは空のタプル) 、二つ目の要素はキーワード引数 (もしくは空の辞書) です。>>> mock = Mock(return_value=None) >>> print(mock.call_args) None >>> mock() >>> mock.call_args call() >>> mock.call_args == () True >>> mock(3, 4) >>> mock.call_args call(3, 4) >>> mock.call_args == ((3, 4),) True >>> mock(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args call(3, 4, 5, key='fish', next='w00t!')
call_args
は、call_args_list
やmethod_calls
、mock_calls
と同様、call
オブジェクトです。これらはタプルとしてアンパックすることで個別に取り出すことができます。そして、より複雑なアサーションを行うことができます。 calls as tuples を参照してください。
-
call_args_list
¶ モックの呼び出しを順に記録したリストです (よって、このリストの長さはモックが呼び出された回数と等しくなります)。モックを作成してから一度も呼び出しを行なっていない場合は、空のリストが返されます。
call
オブジェクトは、call_args_list
の比較対象となる呼び出しのリストを作成する際に便利です。>>> mock = Mock(return_value=None) >>> mock() >>> mock(3, 4) >>> mock(key='fish', next='w00t!') >>> mock.call_args_list [call(), call(3, 4), call(key='fish', next='w00t!')] >>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)] >>> mock.call_args_list == expected True
call_args_list
のメンバはcall
オブジェクトです。タプルとしてアンパックすることで個別に取り出すことができます。 calls as tuples を参照してください。
-
method_calls
¶ 自身の呼び出しと同様に、モックはメソッドや属性、そして それらの メソッドや属性の呼び出しも追跡します:
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.property.method.attribute() <Mock name='mock.property.method.attribute()' id='...'> >>> mock.method_calls [call.method(), call.property.method.attribute()]
method_calls
のメンバはcall
オブジェクトです。タプルとしてアンパックすることで個別に取り出すことができます。 calls as tuples を参照してください。
-
mock_calls
¶ mock_calls
は、メソッド、特殊メソッド、 そして 戻り値のモックまで、モックオブジェクトに対する すべての 呼び出しを記録します。>>> mock = MagicMock() >>> result = mock(1, 2, 3) >>> mock.first(a=3) <MagicMock name='mock.first()' id='...'> >>> mock.second() <MagicMock name='mock.second()' id='...'> >>> int(mock) 1 >>> result(1) <MagicMock name='mock()()' id='...'> >>> expected = [call(1, 2, 3), call.first(a=3), call.second(), ... call.__int__(), call()(1)] >>> mock.mock_calls == expected True
mock_calls
のメンバはcall
オブジェクトです。タプルとしてアンパックすることで個別に取り出すことができます。 calls as tuples を参照してください。
-
__class__
¶ 通常、オブジェクトの
__class__
属性はその型を返します。spec
を設定したオブジェクトの場合、__class__
は代わりにspec
のクラスを返します。これにより、置き換え / 偽装しているオブジェクトに対するisinstance()
も通過することができます:>>> mock = Mock(spec=3) >>> isinstance(mock, int) True
__class__
は書き換え可能で、isinstance()
を通るために必ず spec を使う必要はありません:>>> mock = Mock() >>> mock.__class__ = dict >>> isinstance(mock, dict) True
-
class
unittest.mock.
NonCallableMock
(spec=None, wraps=None, name=None, spec_set=None, **kwargs)¶ 呼び出しができない
Mock
です。コンストラクタのパラメータはMock
と同様ですが、 return_value や side_effect は意味を持ちません。
spec
か spec_set
にクラスかインスタンスを渡した mock は isinstance()
テストをパスします:
>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True
Mock
クラスは、 特殊メソッドをサポートしています。すべての詳細は magic methods を参照してください。
モッククラスや patch()
デコレータは、任意のキーワード引数を設定できます。 patch()
デコレータへのキーワード引数は、モックが作られる際のコンストラクタに渡されます。キーワード引数は、モックの属性を設定します:
>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'
子の戻り値や副作用も、ドットで表記することで同様に設定できます。呼び出し時に直接ドットのついた名前を使用できないので、作成した辞書を **
でアンパックする必要があります:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
...
KeyError
spec (または spec_set) によって作成された呼び出し可能なモックは、モックへの呼び出しがマッチしたときに仕様オブジェクトのシグネチャを内省します。したがって、引数を位置引数として渡したか名前で渡したかどうかに関わらず、実際の呼び出しの引数とマッチすることができます:
>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)
これは assert_called_with()
, assert_called_once_with()
, assert_has_calls()
and assert_any_call()
にも適用されます。 autospec を使う と、 モックオブジェクトのメソッド呼び出しにも適用されます。
バージョン 3.4 で変更: spec や autospec を用いて生成されたモックオブジェクトは、シグネチャを考慮するようになりました。
-
class
unittest.mock.
PropertyMock
(*args, **kwargs)¶ プロパティもしくはディスクリプタとして使われるためのモックです。
PropertyMock
は、__get__()
と__set__()
メソッドを提供し、戻り値を指定することができます。オブジェクトから
PropertyMock
のインスタンスを取得することは、引数を与えないモックの呼び出しに相当します。設定は、 設定する値を伴った呼び出しになります。>>> class Foo: ... @property ... def foo(self): ... return 'something' ... @foo.setter ... def foo(self, value): ... pass ... >>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo: ... mock_foo.return_value = 'mockity-mock' ... this_foo = Foo() ... print(this_foo.foo) ... this_foo.foo = 6 ... mockity-mock >>> mock_foo.mock_calls [call(), call(6)]
PropertyMock
を直接モックに取り付ける方法は、モックの属性を保存する方法によりうまく動作しません。代わりに、モック型に取り付けてください:
>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
26.5.2.1. 呼び出し¶
モックオブジェクトは呼び出し可能です。呼び出しの戻り値は return_value
属性に設定された値です。デフォルトでは新しいモックオブジェクトを返します。この新しいモックは、属性に最初にアクセスした際に作成されます (明示もしくはモックの呼び出しによって)。 そしてそれは保存され、それ以降は同じものが返されます。
呼び出しはオブジェクトとして call_args
や call_args_list
に記録されます。
もし side_effect
が設定されている場合は、その呼び出しが記録された後に呼び出されます。よって、もし side_effect
が例外を発生させても、その呼び出しは記録されます。
呼び出された際に例外を発生させるモックを作成するためには、 side_effect
を例外クラスかインスタンスにする方法が最もシンプルです:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
もし side_effect
が関数だった場合には、その関数の戻り値がモックを呼び出した際の戻り値になります。 side_effect
関数には、モックの呼び出し時に与えられた引数と同じ物があたえられます。これにより、入力によって動的に値を返すことができます:
>>> def side_effect(value):
... return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]
もし、モックにデフォルトの戻り値 (新しいモック) や設定した値を返して欲しい場合は、2つの方法があります。 side_effect
の内部で mock.return_value
を返すか DEFAULT
を返します:
>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
... return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
... return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3
side_effect
を削除し、デフォルトの挙動を行うようにするには、 side_effect
に None
を設定します:
>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
... return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6
side_effect
には、イテレート可能オブジェクトを設定できます。モックが呼び出されるごとに、イテレート可能オブジェクトから戻り値を得ます (イテレート可能オブジェクトが尽きて StopIteration
が発生するまで):
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
...
StopIteration
もしイテレート可能オブジェクトの要素が例外だった場合には、戻り値として返される代わりに例外が発生します:
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
...
ValueError
>>> m()
66
26.5.2.2. 属性の削除¶
モックオブジェクトは要求に応じて属性を生成することで,任意のオブジェクトとして振る舞うことができます。
hasattr()
の呼び出しの際に False
を返したり,属性にアクセスした際に AttributeError
を発生させたりしたい場合, spec
を用いる方法があります.しかし,この方法は必ずしも便利ではありません.
属性を削除することで, AttributeError
を発生させ,アクセスを “妨げる” ようになります.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
26.5.2.3. Mock の名前と name 属性¶
“name” は Mock
コンストラクタの引数なので、モックオブジェクトが “name” 属性を持つことを望む場合、単に生成時にそれを渡すことはできません。2つの選択肢があります。1つのオプションは configure_mock()
を使用することです:
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'
より単純なオプションはモックの生成後に単に “name” 属性をセットすることです:
>>> mock = MagicMock()
>>> mock.name = "foo"
26.5.2.4. 属性として設定されるモック¶
属性 (もしくは戻り値) に他のモックを設定した場合、このモックは “子” になります。この子に対する呼び出しは、親の method_calls
や mock_calls
に記録されます。これは、子のモックを構成し、親にそのモックを設定する際に有用です。また、親に対して設定したすべての子の呼び出しを記録するため、それらの間の順番を確認する際にも有用です:
>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]
モックが名前をもつ場合は、例外的に扱われます。何らかの理由で “子守り” が発生してほしくないときに、それを防ぐことができます。
>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]
patch()
を用いて作成したモックには、自動的に名前が与えられます。名前を持つモックを設定したい場合には、親に対して attach_mock()
メソッドを呼び出します:
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
[1] | 例外は特殊メソッドと属性だけです (これらは2つのアンダースコアで開始・終了します)。モックはこれらの代わりに |
26.5.3. patcher¶
patch デコレータは、その関数のスコープ内でパッチを適用するオブジェクトに対して使用されます。たとえ例外が発生したとしても、パッチは自動的に解除されます。これらすべての機能は文やクラスのデコレータとしても使用できます。
26.5.3.1. patch¶
注釈
patch()
を使うのは簡単です。重要なのは正しい名前空間に対して patch することです。 where to patch セクションを参照してください。
-
unittest.mock.
patch
(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ patch()
は関数デコレータ、クラスデコレータ、コンテキストマネージャーとして利用できます。関数や with 文の body では、 target は new オブジェクトにパッチされます。関数/with 文が終了すると、パッチは元に戻されます。new が省略された場合、 target は
MagicMock
に置き換えられます。patch()
がデコレータとして利用され、 new が省略された場合、生成された mock はデコレータの対象となる関数の追加の引数として渡されます。patch()
がコンテキストマネージャーとして利用された場合、コンテキストマネージャーが生成した mock を返します。target は
'package.module.ClassName'
の形式の文字列でなければなりません。 target はインポートされ、指定されたオブジェクトが new オブジェクトに置き換えられます。なので、 target はpatch()
を呼び出した環境からインポート可能でなければなりません。 target がインポートされるのは、デコレートした時ではなく、デコレートされた関数が呼び出された時です。patch が
MagicMock
を生成する場合、 spec と spec_set キーワード引数はMagicMock
に渡されます。加えて、
spec=True
もしくはspec_set=True
を渡すことで、モック対象のオブジェクトが spec/spec_set に渡されます。new_callable を使って、 new オブジェクトを生成するために呼び出されるクラスや callable オブジェクトを指定することができます。デフォルトでは
MagicMock
が使われます。より強力な spec の形は autospec です。
autospec=True
を指定した場合、 mock は置換対象となるオブジェクトから得られる spec で生成されます。 mock のすべての属性もまた置換対象となるオブジェクトの属性に応じた spec を持ちます。 mock されたメソッドや関数は引数をチェックし、間違ったシグネチャで呼び出された場合はTypeError
を発生させます。クラスを置き換える mock の場合、その戻り値 (つまりインスタンス) はそのクラスと同じ spec を持ちます。create_autospec()
関数と autospec を使う を参照してください。置換対象ではなく任意のオブジェクトを spec として使うために、
autospec=True
の代わりに、autospec=some_object
と指定することができます。デフォルトでは、
patch()
は存在しない属性を置換しません。create=True
を渡してかつ属性が存在しなければ、パッチを当てた関数が呼ばれる時に patch は属性を作成し、それを後で再び削除します。これは、プロダクションコードがランタイムに作成する属性に対してテストを書くのに役立ちます。これは危険な場合があるため、デフォルトではオフになっています。それをオンにすると、実際には存在しないような API に対して通るテストを書くことができてしまいます!注釈
バージョン 3.5 で変更: モジュールのビルトインにパッチを当てようとしているなら、
create=True
を渡す必要はありません。それはデフォルトで追加されます。patch は
TestCase
のクラスデコレータとして利用できます。この場合そのクラスの各テストメソッドをデコレートします。これによりテストメソッドが同じ patch を共有している場合に退屈なコードを減らすことができます。patch()
はpatch.TEST_PREFIX
で始まるメソッド名のメソッドを探します。デフォルトではこれは'test'
で、unittest
がテストを探す方法とマッチしています。patch.TEST_PREFIX
を設定することで異なる prefix を指定することもできます。patch は with 文を使ってコンテキストマネージャーとして使うこともできます。その場合パッチは with 文のブロック内でのみ適用されます。 “as” を使って、 “as” に続いて指定した変数にパッチされたオブジェクトが代入されます。これは
patch()
が mock オブジェクトを生成するときに便利です。patch()
は任意のキーワード引数を受け取り、それをMock
(あるいは new_callable) の生成時に渡します。異なるユースケースのために、
patch.dict(...)
,patch.multiple(...)
,patch.object(...)
が用意されています。
patch()
を関数デコレータとして利用し、 mock を生成してそれをデコレートされた関数に渡します:
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True
クラスをパッチするとそのクラスを MagicMock
の インスタンス に置き換えます。テスト中のコードからそのクラスがインスタンス化される場合、 mock の return_value
が利用されます。
クラスが複数回インスタンス化される場合、 side_effect
を利用して毎回新しい mock を返すようにできます。もしくは、 return_value に、何でも好きなものを設定できます。
パッチしたクラスの インスタンス のメソッドの戻り値をカスタマイズしたい場合、 return_value
に対して設定しなければなりません。例:
>>> class Class:
... def method(self):
... pass
...
>>> with patch('__main__.Class') as MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... assert Class() is instance
... assert Class().method() == 'foo'
...
spec か spec_set を指定し、 patch()
が クラス を置換するとき、生成された mock の戻り値は同じ spec を持ちます。
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
new_callable 引数は、デフォルトの MagicMock
の代わりに別のクラスをモックとして生成したい場合に便利です。例えば、 NonCallableMock
を利用したい場合:
>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
TypeError: 'NonCallableMock' object is not callable
別のユースケースとしては、オブジェクトを io.StringIO
インスタンスで置き換えることがあります:
>>> from io import StringIO
>>> def foo():
... print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()
patch()
に mock を生成させる場合、たいてい最初に必要なことはその mock をセットアップすることです。幾らかのセットアップは patch の呼び出しから行うことができます。任意のキーワード引数が、生成された mock の属性に設定されます。
>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'
生成された mock の属性のさらに属性、 return_value
side_effect
などもセットアップできます。これらはキーワード引数のシンタックスでは直接指定できませんが、それらをキーとする辞書を **
を使って patch()
に渡すことができます。
>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
...
KeyError
26.5.3.2. patch.object¶
-
patch.
object
(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ オブジェクト (target) の指定された名前のメンバー (attribute) を mock オブジェクトでパッチします。
patch.object()
はデコレータ、クラスデコレータ、コンテキストマネージャーとして利用できます。引数の new, spec, create, spec_set, autospec, new_callable はpatch()
と同じ意味を持ちます。patch()
と同じく、patch.object()
も mock を生成するための任意のキーワード引数を受け取ります。クラスデコレータとして利用する場合、
patch.object()
はpatch.TEST_PREFIX
にしたがってラップするメソッドを選択します。
patch.object()
の呼び出しには3引数の形式と2引数の形式があります。 3引数の場合、 patch 対象のオブジェクト、属性名、その属性を置き換えるオブジェクトを取ります。
2引数の形式では、置き換えるオブジェクトを省略し、生成された mock がデコレート対象となる関数に追加の引数として渡されます:
>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
... SomeClass.class_method(3)
... mock_method.assert_called_with(3)
...
>>> test()
spec, create やその他の patch.object()
の引数は patch()
の引数と同じ意味を持ちます。
26.5.3.3. patch.dict¶
-
patch.
dict
(in_dict, values=(), clear=False, **kwargs)¶ 辞書や辞書のようなオブジェクトにパッチし、テスト後に元の状態に戻します。
in_dict は辞書やその他のマップ型のコンテナです。マップ型の場合、最低限 get, set, del 操作とキーに対するイテレートをサポートしている必要があります。
in_dict に辞書を指定する文字列を渡した場合、それをインポートして取得します。
values は対象の辞書にセットする値を含む、辞書か
(key, value)
ペアの iterable です。clear が true なら、新しい値が設定される前に辞書がクリアされます。
patch.dict()
はまた、任意のキーワード引数を受け取って辞書に設定します。patch.dict()
はコンテキストマネージャー、デコレータ、クラスデコレータとして利用できます。クラスデコレータとして利用する場合、patch.dict()
はpatch.TEST_PREFIX
にしたがってラップするメソッドを選択します。
patch.dict()
を使うと、辞書にメンバーを追加するか、または単にテストが辞書を変更して、その後テストが終了した時にその辞書が確実に復元されるようにすることができます。
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
patch.dict()
を呼び出すときにキーワード引数を使って辞書に値をセットすることができます。
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict()
は辞書ではなくても辞書ライクなオブジェクトに対して使うことができます。対象となるオブジェクトは最低限、 get, set, del そしてイテレーションかメンバーのテストのどちらかをサポートする必要があります。これはマジックメソッドの __getitem__()
, __setitem__()
, __delitem__()
そして __iter__()
か __contains__()
のどちらかに対応します。
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
26.5.3.4. patch.multiple¶
-
patch.
multiple
(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ 1回の呼び出しで複数のパッチを実行します。パッチ対象のオブジェクト (あるいはそのオブジェクトをインポートするための文字列) と、パッチ用のキーワード引数を取ります:
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...
patch.multiple()
に mock を生成させたい場合、キーワード引数の値にDEFAULT
を指定します。この場合生成されたモックはデコレート対象の関数にキーワード引数として渡され、コンテキストマネージャーとして利用された場合は辞書として返します。patch.multiple()
はデコレータ、クラスデコレータ、コンテキストマネージャーとして使えます。引数の spec, spec_set, create, autospec, new_callable はpatch()
の引数と同じ意味を持ちます。これらの引数はpatch.multiple()
によって適用される すべての パッチに対して適用されます。クラスデコレータとして利用する場合、
patch.multiple()
はpatch.TEST_PREFIX
にしたがってラップするメソッドを選択します。
patch.multiple()
に mock を生成させたい場合、キーワード引数の値に DEFAULT
を指定します。この場合生成されたモックはデコレート対象の関数にキーワード引数として渡されます。
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple()
は他の patch
デコレータとネストして利用できますが、キーワード引数は patch()
によって作られる通常の引数の 後ろ で受け取る必要があります。
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
patch.multiple()
がコンテキストマネージャーとして利用される場合、コンテキストマネージャーが返す値は、名前がキーで値が生成された mock となる辞書です。
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
26.5.3.5. patch のメソッド: start と stop¶
すべての patcher は start()
と stop()
メソッドを持ちます。これを使うと、 with 文やデコレータをネストさせずに、 setUp
メソッドで複数のパッチをシンプルに適用させることができます。
これらのメソッドを使うには、 patch()
, patch.object, patch.dict()
を通常の関数のように呼び出して、戻り値の patcher
オブジェクトを保持します。その start()
メソッドでパッチを適用し、 stop()
メソッドで巻き戻すことができます。
patch()
に mock を生成させる場合、 patcher.start
の呼び出しの戻り値として mock を受け取れます。
>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock
この方式の典型的なユースケースは、 TestCase
の setUp
メソッドで複数のパッチを行うことです:
>>> class MyTest(TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
ご用心
この方式を使う場合、必ず stop
メソッドを呼び出してパッチが解除する必要があります。 setUp
の中で例外が発生した場合 tearDown
が呼び出されないので、これは意外に面倒です。 unittest.TestCase.addCleanup()
を使うと簡単にできます:
>>> class MyTest(TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
この方式を使うと patcher
オブジェクトの参照を維持する必要がなくなるという特典も付きます。
patch.stopall()
を利用してすべての start されたパッチを stop することもできます。
-
patch.
stopall
()¶ すべての有効なパッチを stop します。
start
で開始したパッチしか stop しません。
26.5.3.6. ビルトインをパッチする¶
モジュール内の任意のビルトインに対してパッチを当てることができます。以下の例はビルトインの ord()
を修正します:
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
26.5.3.7. TEST_PREFIX¶
すべての patcher はクラスデコレータとして利用できます。この場合、そのクラスのすべてのテストメソッドをラップします。 patcher は 'test'
で始まる名前のメソッドをテストメソッドだと認識します。これはデフォルトで unittest.TestLoader
がテストメソッドを見つける方法と同じです。
他の prefix を使う事もできます。その場合、 patcher にその prefix を patch.TEST_PREFIX
に設定することで教えることができます。
>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
... def foo_one(self):
... print(value)
... def foo_two(self):
... print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3
26.5.3.8. patch デコレータをネストする¶
複数のパッチを行いたい場合、シンプルにデコレータを重ねることができます。
次のパターンのように patch デコレータを重ねることができます:
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
デコレータは下から上へと適用されることに注意してください。これは Python がデコレータを適用する標準的な方法です。テスト関数に渡される生成された mock の順番もこれに一致します。
26.5.3.9. どこにパッチするか¶
patch()
は (一時的に) ある 名前 が参照しているオブジェクトを別のものに変更することで適用されます。任意のオブジェクトには、それを参照するたくさんの名前が存在しえます。なので、必ずテスト対象のシステムが使っている名前に対して patch しなければなりません。
基本的な原則は、オブジェクトが ルックアップ されるところにパッチすることです。その場所はオブジェクトが定義されたところとは限りません。これを説明するためにいくつかの例を挙げます。
次のような構造を持ったプロジェクトをテストしようとしていると仮定してください:
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
いま、 some_function
をテストしようとしていて、そのために SomeClass
を patch()
を使って mock しようとしています。
モジュール b をインポートした時点で、 b は SomeClass
を a からインポートしています。この状態で a.SomeClass
を patch()
を使って mock out してもテストには影響しません。モジュール b はすでに 本物の SomeClass
への参照を持っていて、パッチの影響を受けないからです。
重要なのは、 SomeClass
が使われている (もしくはルックアップされている) 場所にパッチすることです。この場合、 some_function
はモジュール b の中にインポートされた SomeClass
をルックアップしています。なのでパッチは次のようにしなければなりません:
@patch('b.SomeClass')
ですが、別のシナリオとして、module b が from a import SomeClass
ではなく import a
をしていて、 some_function
が a.SomeClass
を利用していたとします。どちらのインポートも一般的なものです。この場合、パッチしたいクラスはそのモジュールからルックアップされているので、 a.SomeClass
をパッチする必要があります:
@patch('a.SomeClass')
26.5.3.10. デスクリプタやプロキシオブジェクトにパッチする¶
patch と patch.object はどちらも デスクリプタ (クラスメソッド、static メソッド、プロパティ) を正しく patch できます。デスクリプタに patch する場合、インスタンスではなく class にパッチする必要があります。これらはまた 幾らかの 属性アクセスをプロキシするオブジェクト、例えば django の setttings オブジェクト に対しても機能します。
26.5.4. MagicMock と magic method のサポート¶
26.5.4.1. magick method をモックする¶
Mock
は、 “magic method” とも呼ばれる、Python のプロトコルメソッドに対する mock もサポートしています。これによりコンテナやその他 Python のプロトコルを実装しているオブジェクトを mock することが可能になります。
magic method は通常のメソッドとはルックアップ方法が異なるので [2], magic method のサポートは特別に実装されています。そのため、サポートされているのは特定の magic method のみです。ほとんど すべてのメソッドをサポートしていますが、足りないものを見つけたら私達に教えてください。
magic method を mock するには、対象の method に対して関数や mock のインスタンスをセットします。もし関数を使う場合、それは第一引数に self
を取る 必要があります [3].
>>> def __str__(self):
... return 'fooble'
...
>>> mock = Mock()
>>> mock.__str__ = __str__
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'fooble'
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__iter__ = Mock(return_value=iter([]))
>>> list(mock)
[]
ユースケースの1つは with
文の中でコンテキストマネージャーとして使われるオブジェクトを mock することです。
>>> mock = Mock()
>>> mock.__enter__ = Mock(return_value='foo')
>>> mock.__exit__ = Mock(return_value=False)
>>> with mock as m:
... assert m == 'foo'
...
>>> mock.__enter__.assert_called_with()
>>> mock.__exit__.assert_called_with(None, None, None)
magic method の呼び出しは method_calls
に含まれませんが、 mock_calls
には記録されます。
注釈
mock を生成するのに spec キーワード引数を使った場合、 spec に含まれない magic method を設定しようとすると AttributeError
が発生します。
サポートしている magic method の完全なリスト:
__hash__
,__sizeof__
,__repr__
,__str__
__dir__
,__format__
,__subclasses__
__floor__
,__trunc__
,__ceil__
比較:
__lt__
,__gt__
,__le__
,__ge__
,__eq__
,__ne__
コンテナメソッド:
__getitem__
,__setitem__
,__delitem__
,__contains__
,__len__
,__iter__
,__reversed__
,__missing__
コンテキストマネージャー:
__enter__
,__exit__
単項算術メソッド:
__neg__
,__pos__
,__invert__
算術メソッド (右辺や in-place のものも含む):
__add__
,__sub__
,__mul__
,__matmul__
,__div__
,__truediv__
,__floordiv__
,__mod__
,__divmod__
,__lshift__
,__rshift__
,__and__
,__xor__
,__or__
,__pow__
算術変換メソッド:
__complex__
,__int__
,__float__
,__index__
デスクリプタメソッド:
__get__
,__set__
,__delete__
pickle:
__reduce__
,__reduce_ex__
,__getinitargs__
,__getnewargs__
,__getstate__
,__setstate__
以下のメソッドは存在しますが、mock が利用している、動的に設定不可能、その他の問題が発生する可能性があるなどの理由で サポートされていません:
__getattr__
,__setattr__
,__init__
,__new__
__prepare__
,__instancecheck__
,__subclasscheck__
,__del__
26.5.4.2. Magic Mock¶
MagicMock
系のクラスは2種類あります: MagicMock
と NonCallableMagicMock
です。
-
class
unittest.mock.
MagicMock
(*args, **kw)¶ MagicMock
はMock
のサブクラスで、ほとんどの magic method のデフォルト実装を提供しています。自分で magic method を構成しなくてもMagicMock
を使うことができます。コンストラクタの引数は
Mock
と同じ意味を持っています。spec か spec_set 引数を利用した場合、 spec に存在する magic method のみ が生成されます。
-
class
unittest.mock.
NonCallableMagicMock
(*args, **kw)¶ callable でないバージョンの
MagicMock
コンストラクタの引数は
MagicMock
と同じ意味を持ちますが、 return_value と side_effect は callable でない mock では意味を持ちません。
MagicMock
が magic method をセットアップするので、あとは通常の方法で構成したり利用したりできます:
>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'
デフォルトでは、多くのプロトコルメソッドは特定の型の戻り値を要求されます。それらのメソッドではその型のデフォルトの戻り値がデフォルトの戻り値として設定されているので、戻り値に興味が有る場合以外は何もしなくても利用可能です。デフォルトの値を変更したい場合は手動で戻り値を設定可能です。
メソッドとそのデフォルト値:
__lt__
: NotImplemented__gt__
: NotImplemented__le__
: NotImplemented__ge__
: NotImplemented__int__
: 1__contains__
: False__len__
: 0__iter__
: iter([])__exit__
: False__complex__
: 1j__float__
: 1.0__bool__
: True__index__
: 1__hash__
: mock のデフォルトの hash__str__
: mock のデフォルトの str__sizeof__
: mock のデフォルトの sizeof
例えば:
>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False
2つの比較メソッド __eq__()
と __ne__()
は特別です。それらは、もし戻り値として何か別のものを返すように変更していなければ、 side_effect
属性を使用して、同一性に基づくデフォルトの同値比較を行います:
>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True
MagickMock.__iter__()
の return_value は任意の iterable で、イテレータである必要はありません:
>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
return_value が iterator であった場合、最初のイテレートでその iterator を消費してしまい、2回目以降のイテレートの結果が空になってしまいます:
>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]
MagicMock
はいくつかの曖昧であったり時代遅れなものをのぞいて、対応している magic method を事前にセットアップします。自動でセットアップされていないものも必要なら手動でセットアップすることができます。
MagicMock
がサポートしているもののデフォルトではセットアップしない magic method:
__subclasses__
__dir__
__format__
__get__
,__set__
,__delete__
__reversed__
,__missing__
__reduce__
,__reduce_ex__
,__getinitargs__
,__getnewargs__
,__getstate__
,__setstate__
__getformat__
,__setformat__
[2] | Magic method はインスタンスではなくクラスからルックアップされるはずです。Python のバージョンによってこのルールが適用されるかどうかに違いがあります。サポートされているプロトコルメソッドは、サポートされているすべての Python のバージョンで動作するはずです。 |
[3] | 関数はクラスまで hook しますが、各 |
26.5.5. ヘルパー¶
26.5.5.1. sentinel¶
-
unittest.mock.
sentinel
¶ sentinel
オブジェクトはテストで必要なユニークなオブジェクトを簡単に提供します。属性はアクセス時にオンデマンドで生成されます。同じ属性に複数回アクセスすると必ず同じオブジェクトが返されます。返されるオブジェクトは、テスト失敗のメッセージがわかりやすくなるように気が利いた repr を持ちます。
The
sentinel
attributes don’t preserve their identity when they arecopied
orpickled
.
特定のオブジェクトが他のメソッドに引数として渡されることをテストしたり、返されることをテストしたい場合があります。このテストのために名前がついた sentinel オブジェクトを作るのが一般的です。 sentinel
はこのようなオブジェクトを生成し、同一性をテストするのに便利な方法を提供します。
次の例では、 method
が sentinel.some_object
を返すようにモンキーパッチしています:
>>> real = ProductionClass()
>>> real.method = Mock(name="method")
>>> real.method.return_value = sentinel.some_object
>>> result = real.method()
>>> assert result is sentinel.some_object
>>> sentinel.some_object
sentinel.some_object
26.5.5.2. DEFAULT¶
-
unittest.mock.
DEFAULT
¶ DEFAULT
オブジェクトは事前に生成された sentinel (実際にはsentinel.DEFAULT
) オブジェクトです。side_effect
関数が、通常の戻り値を使うことを示すために使います。
26.5.5.3. call¶
-
unittest.mock.
call
(*args, **kwargs)¶ call()
はcall_args
,call_args_list
,mock_calls
,method_calls
と比較してより下端に assert できるようにするためのヘルパーオブジェクトです。call()
はassert_has_calls()
と組み合わせて使うこともできます。>>> m = MagicMock(return_value=None) >>> m(1, 2, a='foo', b='bar') >>> m() >>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()] True
-
call.
call_list
()¶ 複数回の呼び出しを表す call オブジェクトに対して、
call_list()
はすべての途中の呼び出しと最終の呼び出しを含むリストを返します。
call_list
は特に “chained call” に対して assert するのに便利です。 “chained call” は1行のコードにおける複数の呼び出しです。この結果は mock の mock_calls
に複数の call エントリとして格納されます。この call のシーケンスを手動で構築するのは退屈な作業になります。
call_list()
は同じ chained call からその call のシーケンスを構築することができます:
>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
call().method(arg='foo'),
call().method().other('bar'),
call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True
call
オブジェクトは、どう構築されたかによって、 (位置引数、キーワード引数) のタプルか、 (名前、位置引数、キーワード引数) のタプルになります。自分で call オブジェクトを構築するときはこれを意識する必要はありませんが、 Mock.call_args
, Mock.call_args_list
, Mock.mock_calls
属性の中の call
オブジェクトを解析して個々の引数を解析することができます。
Mock.call_args
と Mock.call_args_list
の中の call
オブジェクトは (位置引数, キーワード引数) のタプルで、 Mock.mock_calls
の中の call
オブジェクトや自分で構築したオブジェクトは (名前, 位置引数, キーワード引数) のタプルになります。
call オブジェクトの “タプル性” を使って、より複雑な内省とアサートを行うために各引数を取り出しすことができます。位置引数はタプル (位置引数が存在しない場合は空のタプル) で、キーワード引数は辞書になります:
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> args, kwargs = kall
>>> args
(1, 2, 3)
>>> kwargs
{'arg2': 'two', 'arg': 'one'}
>>> args is kall[0]
True
>>> kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg2': 'three', 'arg': 'two'}
>>> name is m.mock_calls[0][0]
True
26.5.5.4. create_autospec¶
-
unittest.mock.
create_autospec
(spec, spec_set=False, instance=False, **kwargs)¶ 他のオブジェクトを spec として利用して mock オブジェクトを作ります。 mock の属性も、 spec オブジェクトの該当する属性を spec として利用します。
mock された関数やメソッドは、正しいシグネチャで呼び出されたことを確認するために引数をチェックします。
spec_set が
True
のとき、 spec オブジェクトにない属性をセットしようとするとAttributeError
を発生させます。spec にクラスが指定された場合、 mock の戻り値 (そのクラスのインスタンス) は同じ spec を持ちます。
instance=True
を指定すると、インスタンスオブジェクトの spec としてクラスを利用できます。返される mock は、モックのインスタンスが callable な場合にだけ callable となります。create_autospec()
は任意のキーワード引数を受け取り、生成する mock のコンストラクタに渡します。
create_autospec()
や、 patch()
の autospec 引数で autospec を使うサンプルは autospec を使う を参照してください。
26.5.5.5. ANY¶
-
unittest.mock.
ANY
¶
mock の呼び出しのうち 幾つか の引数に対して assert したいけれども、それ以外の引数は気にしない、あるいは call_args
から個別に取り出してより高度な assert を行いたい場合があります。
特定の引数を無視するために、 すべて と等しくなるオブジェクトを使うことができます。そうすると、 assert_called_with()
と assert_called_once_with()
は、実際の引数が何であったかに関わらず成功します。
>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)
mock_calls
などの call list との比較に ANY
を使うこともできます:
>>> m = MagicMock(return_value=None)
>>> m(1)
>>> m(1, 2)
>>> m(object())
>>> m.mock_calls == [call(1), call(1, 2), ANY]
True
26.5.5.6. FILTER_DIR¶
-
unittest.mock.
FILTER_DIR
¶
FILTER_DIR
は mock オブジェクトが dir()
に何を返すかを制御するためのモジュールレベル変数です。 (Python 2.6 以上でのみ有効) デフォルトは True
で、以下に示すフィルタリングを行い、有用なメンバーだけを表示します。このフィルタリングが嫌な場合や、何かの診断のためにフィルタリングを切りたい場合は、 mock.FILTER_DIR = False
と設定してください。
フィルタリングが有効な場合、 dir(some_mock)
は有用な属性だけを表示し、また通常は表示されない動的に生成される属性も表示します。 mock が spec を使って(もちろん autospec でも)生成された場合、元のオブジェクトのすべての属性が、まだアクセスされていなかったとしても、表示されます。
>>> dir(Mock())
['assert_any_call',
'assert_called_once_with',
'assert_called_with',
'assert_has_calls',
'attach_mock',
...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
'AbstractDigestAuthHandler',
'AbstractHTTPHandler',
'BaseHandler',
...
多くのあまり有用ではない (mock 対象のものではなく、 Mock
自身のプライベートな) 属性は、アンダースコアと2つのアンダースコアで prefix された属性は Mock
に対して dir()
した結果からフィルタリングされます。この動作が嫌な場合は、モジュールレベルの FILTER_DIR
スイッチを設定することでフィルターを切ることができます。
>>> from unittest import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_NonCallableMock__get_return_value',
'_NonCallableMock__get_side_effect',
'_NonCallableMock__return_value_doc',
'_NonCallableMock__set_return_value',
'_NonCallableMock__set_side_effect',
'__call__',
'__class__',
...
mock.FILTER_DIR
によるフィルタリングをバイパスしたい場合、 var(my_mock)
(インスタンスメンバー)、 dir(type(my_mock))
(型メンバー) を代わりに使うことができます。
26.5.5.7. mock_open¶
-
unittest.mock.
mock_open
(mock=None, read_data=None)¶ open()
の利用を置き換えるための mock を作るヘルパー関数。open()
を直接呼んだりコンテキストマネージャーとして利用する場合に使うことができます。mock 引数は構成する mock オブジェクトです。
None
(デフォルト) なら、通常のファイルハンドルと同じ属性やメソッドにAPIが制限されたMagicMock
が生成されます。read_data は、ファイルハンドルの
read()
,readline()
, そしてreadlines()
のメソッドが返す文字列です。これらのメソッドを呼び出すと、読み出し終わるまで read_data からデータが読み出されます。これらモックのメソッドはとても単純化されています: mock が呼ばれるたびに read_data は先頭に巻き戻されます。テストコードに与えるデータをさらにコントロールするには自分自身でモックをカスタマイズする必要があります。それでも不十分な場合は、 PyPI にあるインメモリファイルシステムパッケージのうちのどれかを使えば、テストのための本物のファイルシステムが得られるでしょう。バージョン 3.4 で変更:
readline()
およびreadlines()
のサポートが追加されました。read()
のモックは、個々の呼び出しで read_data を返すのではなく、それを消費するように変わりました。バージョン 3.5 で変更: read_data は mock を呼び出す度に毎回リセットされるようになりました。
open()
をコンテキストマネージャーとして使う方法は、ファイルが必ず適切に閉じられるようにする素晴らしい方法で、今では一般的になっています:
with open('/some/path', 'w') as f:
f.write('something')
問題は、 open()
をモックアウトしたところで、コンテキストマネージャーが使われる (__enter__()
と __exit__()
が呼ばれる) のはその 戻り値 だということです。
コンテキストマネージャーを MagicMock
でモックするのは一般的かつ面倒なので、ヘルパー関数を用意しています。
>>> m = mock_open()
>>> with patch('__main__.open', m):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
ファイルの読み込みをモックする例:
>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'
26.5.5.8. autospec を使う¶
autospec は mock の spec
機能を基盤にしています。 autospec は mock の API を元のオブジェクト (spec) に制限しますが、再帰的に適用される (lazy に実装されている) ので、 mock の属性も spec の属性と同じ API だけを持つようになります。さらに、 mock された関数/メソッドは元と同じシグネチャを持ち、正しくない引数で呼び出されると TypeError
を発生させます。
autospec の動作について説明する前に、それが必要となる背景から説明していきます。
Mock
は非常に強力で柔軟なオブジェクトですが、テスト対象のシステムをモックアウトするときに2つの欠点があります。 1つ目の欠点は Mock
の API に関したもので、もう一つは mock オブジェクトを使う場合のもっと一般的な問題です。
まず Mock
独自の問題から解説します。 Mock
は便利な2つのメソッド、 assert_called_with()
と assert_called_once_with()
を持っています。
>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
Traceback (most recent call last):
...
AssertionError: Expected 'mock' to be called once. Called 2 times.
mock が属性をオンデマンドに自動生成し、それを任意の引数で呼び出せるため、それらの assert メソッドのいずれかをミススペルするとその assert の効果が消えてしまいます:
>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assret_called_once_with(4, 5, 6)
typo のために、テストは不正確に、かつ暗黙に成功してしまいます。
2つ目の問題はもっと一般的なものです。なにかのコードをリファクタし、メンバの名前を変更したとします。古いAPI を利用したコードに対するテストが、mock を利用しているとするとテストは通り続けます。このため、コードが壊れていてもテストがすべて通ってしまう可能性があります。
各ユニットが互いにどのように接続されるかをテストしない場合、依然としてテストで見つけることができるバグの余地が多く残っています。
mock
はこの問題に対処するために spec と呼ばれる機能を提供しています。何かクラスかインスタンスを spec
として mock に渡すと、実際のクラスに存在する属性にしか、 mock に対してもアクセスできなくなります:
>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
spec はその mock 自体にしか適用されません。なので、同じ問題がその mock のすべてのメソッドに対して発生します:
>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()
autospec はこの問題を解決します。 patch()
か patch.object()
に autospec=True
を渡すか、 create_autospec()
関数を使って spec をもとに mock を作ることができます。 patch()
の引数に autospec=True
を渡した場合、置換対象のオブジェクトが spec オブジェクトとして利用されます。 spec は遅延処理される (mock の属性にアクセスされた時に spec が生成される) ので、非常に複雑だったり深くネストしたオブジェクト (例えばモジュールをインポートするモジュールをインポートするモジュール) に対しても大きなパフォーマンスの問題なしに autospec を使うことができます。
autospec の利用例:
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
request.Request
が spec を持っているのが分かります。 request.Request
のコンストラクタは2つの引数を持っています (片方は self です)。コンストラクタを間違って呼び出した時に何が起こるでしょうか:
>>> req = request.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
spec はクラスがインスタンス化されたとき (つまり spec が適用された mock の戻り値) にも適用されます:
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Request
オブジェクトは callable ではないので、 request.Request
の mock から返されたインスタンスの mock は callable ではなくなります。 spec があれば、 assert のミススペルは正しいエラーを発生させます:
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
多くの場合、単に既存の patch()
呼び出しに autospec=True
を加えるだけで、ミススペルやAPI変更に伴うバグから守られます。
patch()
を経由する以外にも、 create_autospec()
を使って autospec が適用された mock を直接作る方法もあります:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
とはいえ、autospec には注意しなければならない点や制限があり、そのためデフォルトでは無効になっています。spec オブジェクトでどんな属性が使えるかどうかを調べるために、autospec は spec オブジェクトをイントロスペクト (実際に属性にアクセスする) 必要があります。mock の属性を利用するとき、水面下で元のオブジェクトに対しても同じ属性の探索が行われます。spec したオブジェクトのどれかがコードを実行するプロパティやデスクリプタを持っている場合、autospec は正しく動きません。もしくは、イントロスペクションしても安全なようにオブジェクトを設計するのがよいでしょう [4]。
より重大な問題は、インスタンス属性が __init__()
で生成され、クラスには全く存在しない場合です。 autospec は動的に生成される属性については知ることができず、 API を検出できる属性だけに制限してしまいます。
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
この問題を解決するためにいくつかの方法があります。一番簡単な、ただし一番面倒でないとは限らない方法は、必要とされる属性を mock が生成された後に設定することです。 autospec は属性を参照することを禁止しますが、設定することは禁止していません:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
spec と autospec にはよりアグレッシブなバージョンがあり、存在しない属性への設定も禁止します。これはコードが正しい属性にのみ代入することを保証したいときに便利ですが、もちろん先ほどの方法も制限されてしまいます:
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
この問題を解決するベストな方法は、 __init__()
で初期化されるインスタンスメンバに対する初期値をクラス属性として追加することかもしれません。 __init__()
メソッドの中でデフォルトの属性を代入しているだけなら、それをクラス属性にする (この属性はもちろんインスタンス間で共有されます) 方が速くなるのもメリットです。例:
class Something:
a = 33
クラス属性を使ってもまた別の問題があります。メンバーのデフォルト値に None
を利用し、後から別の型のオブジェクトを代入するのは比較的よくあるパターンです。 spec として None
を使うと すべての 属性やメソッドへのアクセスも許されなくなるので使い物になりません。 None``を spec にすることが有用な場面は *決して* なく、おそらくそのメンバーは他の何かの型のメンバーになることを示すので、 autospec は ``None
に設定されているメンバーには spec を使いません。その属性は通常の mock (MagicMocks) になります。
>>> class Something:
... member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>
すでに利用されているクラスにデフォルト値属性を追加するのが嫌な場合は、他の選択肢もあります。選択肢の1つは、クラスではなくインスタンスを spec に使うことです。別の選択肢は、実際のクラスのサブクラスを作り、実際に利用されている方に影響を与えずにデフォルト値属性を追加することです。どちらの方法も spec として代替オブジェクトを利用することが必要です。 patch()
はこれをサポートしていて、 autospec 引数に代替オブジェクトを渡すことができます。
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
[4] | これはクラスやすでにインスタンス化されたオブジェクトにだけ当てはまります。 mock されたクラスを呼び出して mock インスタンスを作っても、実際のオブジェクトのインスタンスは生成されません。
mock は属性を - |