web-dev-qa-db-ja.com

クラスのモック:Mock()またはpatch()?

私はPythonで mock を使用しており、これらの2つのアプローチのどちらが優れているか疑問に思っていました(読む:よりPythonic)。

方法1:モックオブジェクトを作成して使用するだけです。コードは次のようになります。

def test_one (self):
    mock = Mock()
    mock.method.return_value = True 
    self.sut.something(mock) # This should called mock.method and checks the result. 
    self.assertTrue(mock.method.called)

方法2:パッチを使用してモックを作成します。コードは次のようになります。

@patch("MyClass")
def test_two (self, mock):
    instance = mock.return_value
    instance.method.return_value = True
    self.sut.something(instance) # This should called mock.method and checks the result. 
    self.assertTrue(instance.method.called)

どちらの方法も同じことを行います。違いがわかりません。

誰でも私を啓発できますか?

103
Sardathrion

mock.patch は、mock.Mockとは非常に異なる生き物です。 patchクラスをモックオブジェクトに置き換え、モックインスタンスを操作できます。このスニペットを見てください:

>>> class MyClass(object):
...   def __init__(self):
...     print 'Created MyClass@{0}'.format(id(self))
... 
>>> def create_instance():
...   return MyClass()
... 
>>> x = create_instance()
Created MyClass@4299548304
>>> 
>>> @mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
...   MyClass.return_value = 'foo'
...   return create_instance()
... 
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
...   print MyClass
...   return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass@4300234128
<__main__.MyClass object at 0x100505d90>

patchは、MyClassを置き換えて、呼び出す関数でクラスの使用を制御できるようにします。クラスにパッチを適用すると、クラスへの参照は完全にモックインスタンスに置き換えられます。

mock.patchは通常、テスト内でクラスの新しいインスタンスを作成する何かをテストするときに使用されます。 mock.Mockインスタンスはより明確で優先されます。 self.sut.somethingメソッドがインスタンスをパラメーターとして受け取る代わりにMyClassのインスタンスを作成した場合、ここではmock.patchが適切です。

141
D.Shawley

これについて YouTubeビデオ があります。

簡単な答え:モックしたいものを渡す場合はmockを使用し、そうでない場合はpatchを使用します。 2つのうち、適切な依存性注入を使用してコードを記述していることを意味するため、モックが強く推奨されます。

愚かな例:

# Use a mock to test this.
my_custom_Tweeter(Twitter_api, sentence):
    sentence.replace('cks','x')   # We're cool and hip.
    Twitter_api.send(sentence)

# Use a patch to mock out Twitter_api. You have to patch the Twitter() module/class 
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_Tweeter(sentence):
    Twitter_api = Twitter(user="XXX", password="YYY")
    sentence.replace('cks','x') 
    Twitter_api.send(sentence)
18
MikeTwo