En Python, ¿cuál es un buen patrón para deshabilitar cierto código durante las pruebas unitarias?

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 ese código “hey, BTW , Estoy haciendo una prueba de unidad, ¿puede por favor no hacer su llamada a solr, en su lugar, por favor, puede pegar lo que enviaría a solr en este lugar para que pueda verificarlo “. Tengo mis ideas, pero no me gustan ninguna de ellas, espero que haya una buena forma pythonica de hacer esto.

Usa el Mock de Michael Foord en tu prueba de unidad, haz 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") 

Puede usar objetos simulados para interceptar las llamadas de método que no desea ejecutar. Por ejemplo, tiene alguna clase A , donde no desea que se llame al método no() durante una prueba.

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

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

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

Luego, crearía objetos MockA lugar de A s en su código de prueba. Otra forma de hacer burla sería que A y MockA implementen una interfaz común, como InterfaceA .

Hay toneladas de marcos de burla disponibles. Ver StackOverflow: marcos de mocking de Python .

En particular, ver: marco de burla Python de Google .

El gran problema que estaba teniendo era con la mecánica de la dependency injection. Ahora he descubierto esa parte.

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

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

Parece que no puedo hacer esto en el corredor de mi prueba:

 from foo import solr solr = mock_object 

El intérprete de python debe tratar los módulos foo_service.foo y foo como entradas diferentes. Cambié from foo import solr a más explícito from foo_service.foo import solr y mi objeto simulado se inyectó con éxito.

Por lo general, cuando surge algo como esto, usa Monkey Patching (también llamado Duck Punching) para lograr los resultados deseados. Echa un vistazo a este enlace para obtener más información sobre Monkey Patching.

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

Tiene dos formas de hacerlo: no , o como mínimo, en el caso de DI , modificaciones a su código fuente

  • Inyección de dependencia
  • Parches de mono

La forma más limpia es usar la dependency injection , pero en realidad no me gustan las plots extensas , y hay algunas cosas que no son posibles / difíciles de hacer que la dependency injection facilita.

Sé que es el caso de uso típico de los objetos simulados, pero ese también es un viejo argumento … ¿Son necesarios los objetos simulados o son malvados ?

Estoy del lado de aquellos que creen que las burlas son malvadas 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 de código …

Si desea cambiar o interceptar una llamada de función interna para fines de prueba, también puede hacer que esta función sea un conjunto de dependencias externas explícitas en el momento de la instancia que proporcionaría tanto su código de producción como su código de prueba. Si haces eso, el problema desaparece y terminas con una interfaz más limpia.

Tenga en cuenta que al hacerlo no es necesario cambiar el código probado en absoluto ni internamente ni por la prueba que se está realizando.