2012-08-31 8 views
12

mockを使って、Pythonで単体テストを書くことを試みます。例えばPythonモックライブラリで基本クラスをモックする方法

私は以下のクラスがあります。

class TCPHandler(socketserver.BaseRequestHandler): 
    def handle(self): 
     self.data = self.request.recv(1024).strip() 

をそして、私は唯一のhandleメソッドをテストしたいです。 socketserver.BaseRequestHandlerについて何も想定する必要はありません。私はたとえば、handleがという引数でrecvを呼び出すと主張したいと思います。そんなことをモックですることは可能ですか?私。基本クラスsocketserver.BaseRequestHandlerをモックに置き換えますか?それとも私はそのアイデアを追跡していませんか? ecatmurの答えを


(ありがとう!)私が最初に次のことを試してみました:

patcher = patch.object(TCPHandler, '__bases__', (Mock,)) 
with patcher: 
    patcher.is_local = True 
    handler = TCPHandler() 
    handler.handle() 

しかし、今handleはanylongerと呼ばれ、dir(handler)ができますされていません。

['assert_any_call', 'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 'attach_mock', 'call_args', 'call_args_list', 'call_count', 'called', 'configure_mock', 'method_calls', 'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 'side_effect'] 

type(handler)ができます <class 'mock.TCPHandler'>

私は基本クラスにパッチを当てると、派生したクラスも疑似クラスになります。


私は今、別のアイデアを試して与えた:

mock = MagicMock() 
TCPHandler.handle(mock) 
#assertions 

モックが呼び出されないようですが。

答えて

17

あなたは、派生クラスの__bases__にパッチを適用することによってこれを行うことができます。

def test_derived(): 
    patcher = mock.patch.object(Derived, '__bases__', (mock.Mock,)) 
    with patcher: 
     patcher.is_local = True 
     d = Derived() 
     print d.foo() 

is_localハックパッチを反転するときdelattrを呼び出そうからmock.patchを停止する必要があります。

+0

私はあなたのヒントに従いましたが、その結果、派生クラスのメソッドもモックに置き換えられました。 –

+0

私は現在のテストコードを質問に入れました。私はあなたの助けに感謝します。 –

+0

@FrederickRothはあまり確かではありません。 Python 2ではうまく動作します。おそらく、メソッドがアクセスするオブジェクトの部分を嘲笑しようとしますか? – ecatmur

4

私は実際にあなたがテストしたい実際のコードを模擬しようとしていると思います。そのコードで呼び出されているオブジェクトではありません。 handleメソッドがself.requestでrecvメソッドを呼び出すかどうかを調べたい場合は、recvメソッドを削除してください。

def test_tcp_handler_method(self): 

    handler = TCPHandler() 
    handler.request = Mock() 

    handler.handle() 

    self.assertTrue(handler.request.recv.called) 
    self.assertEqual(handler.request.recv.call_args[0], 1024) 

あなたはインスタンス化するハンドラを取得するために、いくつかの余分な設定を行う必要があるかもしれないが、基本的な考え方は明確にする必要があります。

+0

あなたのご意見ありがとうございます! TCPHandlerはインスタンス化するためにtcprequestを必要とし、したがって私のコードではなく、TCPServerによって直接インスタンス化されることはありません。これが私が '__init__'を呼びたくない理由の全体です。私はちょうどtcpserverフレームワーク全体をテストせずに私自身の小さなメソッドをテストしたいだけです。 –

4

私はそれが最良の解決策であるかどうかわかりませんが、type()を使用して別の親を使って前のクラスを再定義することができました。

from contextlib import contextmanager 

@contextmanager 
def patch_parent(class_): 
    """ 
    Mock the bases 
    """ 
    yield type(class_.__name__, (Mock,), dict(class_.__dict__)) 

はこの後、あなたが使用することができます。このようなpatch_parent

class Bar(): 
    def method(self, param1, param2...): 
     ... 

class Foo(Bar): 
    pass 


>>> with patch_parent(Foo) as MockFoo: 
...  f = MockFoo() 
...  print f 
...  print f.method() 
... 
<Foo id='15488016'> 
<Foo name='mock.method()' id='15541520'> 
>>> s = Foo() 
>>> s.method() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: method() takes exactly 3 arguments (1 given) 

MockFooクラスがまだのメソッドを持っている私は、親のモックを持つクラスを返すpatch_parent()と呼ばれる機能を、構築されましたFooクラスであり、親が今度はMockクラスであるため、親に定義されたメソッドはありません。

関連する問題