2010-02-23 13 views
10

En general, quiero desactivar el menor código posible, y quiero que sea explícito: no quiero que el código que se está probando decida si es una prueba o no, quiero que la prueba diga que el código "hey" Por cierto, estoy ejecutando una prueba de unidad, ¿pueden hacer su llamada a Solr? En su lugar, pueden poner lo que enviarían a Solr en este lugar para que pueda verificarlo ". Tengo mis ideas, pero no me gustan. Espero que haya una buena manera pitónica de hacerlo.En Python, ¿cuál es un buen patrón para deshabilitar cierto código durante las pruebas unitarias?

Respuesta

5

Uso Michael Foord de Mock en su unidad de prueba a hacer esto:

from mock import Mock 

class Person(object): 
    def __init__(self, name): 
     super(Person, self).__init__() 
     self.name = name 

    def say(self, str): 
     print "%s says \"%s\"" % (self.name, str) 


... 

#In your unit test.... 
#create the class as normal 
person = Person("Bob") 
#now mock all of person's methods/attributes 
person = Mock(spec=person) 
#talkto is some function you are testing 
talkTo(person) 
#make sure the Person class's say method was called 
self.assertTrue(person.say.called, "Person wasn't asked to talk") 

#make sure the person said "Hello" 
args = ("Hello") 
keywargs = {} 
self.assertEquals(person.say.call_args, (args, keywargs), "Person did not say hello") 
+0

¡Guau, simulacro es increíble! Gracias. En mis días de C++/Java solía pasar demasiado tiempo escribiendo clases simuladas a mano. – guidoism

+0

Sí, no es broma! Acabo de enterarme de esto leyendo esta pregunta. Definitivamente lo agregué al libro de trucos – manifest

+1

Desde Python 3.3, hay un módulo 'unittest.mock' [en la biblioteca estándar] (https://docs.python.org/ 3/library/unittest.mock.html) –

7

Puede usar Mock objects para interceptar las llamadas a métodos que no desea ejecutar. P. ej. Tiene alguna clase A, donde no desea llamar al método no() durante una prueba.

class A: 
    def do(self): 
    print('do') 
    def no(self): 
    print('no') 

Un objeto de burla podría heredar de A y anular no() no hacer nada.

class MockA(A): 
    def no(self): 
    pass 

A continuación, crear MockA objetos en lugar de A s en su código de prueba. Otra forma de burlarse sería tener A y MockA implementando una interfaz común, digamos InterfaceA.

Hay toneladas de marcos burlones disponibles. Ver StackOverflow: Python mocking frameworks.

En particular, ver: Google's Python mocking framework.

+2

La creación de subclase para implementar la burla trae potencialmente todo tipo de cosas que no necesita. Además, sus métodos no toman el primer argumento requerido –

+0

Wow, realmente estoy cavando Mock de Michael Foord. Es hermoso lo fácil que es crear objetos simulados en Python. Si ha tenido que hacer esto manualmente en C++ utilizando la interfaz y la herencia, sabrá cuán doloroso podría ser esto. EDITAR: Por cierto, esta respuesta realmente no es correcta para Mock, pero señala el camino hacia lo que podrías estar buscando. Ver [http://www.voidspace.org.uk/python/mock/examples.html#creating-a-mock-from-an-existing-object] – manifest

+0

@Mike Graham, gracias por señalar el argumento que falta. No digo que la subclasificación sea la mejor manera de burlarse, solo estaba dando un ejemplo. –

0

Normalmente, cuando ocurre algo como esto, usa Monkey Patching (también llamado Duck Punching) para lograr los resultados deseados. Consulte this link para obtener más información sobre Monkey Patching.

En este caso, por ejemplo, sobrescribiría solr para simplemente imprimir la salida que está buscando.

0

Sé que es el caso de uso típico para los objetos de imitación, pero eso es también una vieja discusión ... son objetos necesarios Mock en absoluto o ¿son evil?

Estoy del lado de aquellos que creen que los simulacros son evil y tratarían de evitar cambiar el código probado en absoluto. Incluso creo que tal necesidad de modificar el código probado es un olor a código ...

Si desea cambiar o interceptar una llamada de función interna para fines de prueba, también podría hacer de esta función una dependencia externa explícita configurada en instanciation time que sería proporcionada tanto por su código de producción como por código de prueba. Si lo haces, el problema desaparecerá y terminarás con una interfaz más limpia.

Tenga en cuenta que al hacerlo no es necesario cambiar el código probado ni internamente ni mediante la prueba que se realiza.

1

El gran problema que tuve fue con la mecánica de la inyección de dependencia. Ahora me di cuenta de esa parte.

Necesito importar el módulo exactamente de la misma manera en ambos lugares para insertar con éxito el nuevo código. Por ejemplo, si tengo el siguiente código que desee desactivar:

from foo_service.foo import solr 
solr.add(spam) 

Me parece que no puede hacer esto en el corredor de prueba en mi:

from foo import solr 
solr = mock_object 

el intérprete de Python debe ser el tratamiento de los módulos foo_service.foo y foo como entradas diferentes. Cambié from foo import solr al más explícito from foo_service.foo import solr y mi objeto simulado fue inyectado con éxito.

Cuestiones relacionadas