2012-08-31 9 views
12

Intento usar mock para escribir algunas pruebas de unidad en python.Cómo simular una clase base con la biblioteca de simulacro de pitón

Por ejemplo, tengo la clase siguiente:

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

Y sólo quieren probar el método handle. Sin tener que asumir nada sobre socketserver.BaseRequestHandler. Por ejemplo, quiero afirmar que handle llama al recv con el argumento 1024. ¿Es posible hacer tal cosa con simulacro? Es decir. reemplazando la clase base socketserver.BaseRequestHandler con un simulacro? ¿O estoy desviado de esa idea?


Con la respuesta de ecatmur (¡gracias!) Intentó por primera vez el siguiente:

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

Pero ahora handle no se llama anylonger y dir(handler) da:

['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) da <class 'mock.TCPHandler'>

Lo que interpreto que parchear la clase base también convierte mi clase derivada en una simulación.


ahora me dio otra idea intentarlo:

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

Sin embargo la maqueta parece no ser llamado.

Respuesta

17

Usted puede hacer esto mediante parches de __bases__ la clase derivada:

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

El truco is_local es necesario detener mock.patch de tratar de llamar delattr al dar marcha atrás el parche.

+0

Seguí tu sugerencia pero la consecuencia fue que también el método de la clase derivada se reemplazó por el simulacro. –

+0

Puse mi código de prueba actual en la pregunta, si es que podría echar un vistazo? Aprecio tu ayuda. –

+0

@FrederickRoth no está demasiado seguro; funciona bien para mí con Python 2. ¿Quizás intente burlarse de las partes del objeto al que accede el método? – ecatmur

4

Creo que el problema es que realmente intentas burlarte del código real que deseas probar. En lugar de los objetos que están siendo llamados por ese código. Si está interesado en ver si el método handle llama al método recv en self.request, simule el método 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) 

Es posible que tenga que hacer una configuración adicional para que el manejador cree una instancia, pero la idea básica debe ser clara.

+0

¡Gracias por tu aporte! El TCPHandler necesita una tcprequest para crear instancias y, por lo tanto, nunca es instanciado directamente por mi código, sino por un servidor TCPS. Esta es la razón por la cual no quiero llamar su '__init__'. Solo quiero probar mi propio método pequeño sin probar también todo el framework tcpserver. –

4

No sé si es la mejor solución pero logré redefinir la clase anterior con un elemento primario diferente usando type().Construí una función llamada patch_parent(), que devuelve la clase con una maqueta de los padres:

from contextlib import contextmanager 

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

Después de esto, se puede utilizar el patch_parent así:

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) 

La clase MockFoo todavía tiene los métodos de la Foo clase y no tiene los métodos definidos en el padre porque el padre es ahora una clase Mock.

Cuestiones relacionadas