Usar un parche simulado para simular un método de instancia

82

Estoy tratando de burlarme de algo mientras pruebo una aplicación Django usando la biblioteca de pruebas Mock con nombre imaginativo . Parece que no puedo hacer que funcione, estoy tratando de hacer esto:

models.py

from somelib import FooClass

class Promotion(models.Model):
    foo = models.ForeignKey(FooClass)
    def bar(self):
       print "Do something I don't want!"


test.py

class ViewsDoSomething(TestCase):
    view = 'my_app.views.do_something'

    def test_enter_promotion(self):
        @patch.object(my_app.models.FooClass, 'bar')
        def fake_bar(self, mock_my_method):
            print "Do something I want!"
            return True

        self.client.get(reverse(view))

¿Qué estoy haciendo mal?

Kit Sunde
fuente
bares de hecho un "método de instancia" y su único parámetro es self. Para ser un método de clase, tendría que estar parametrizado con clsy sería invocable como Promotion.foo().
Cavaunpeu
El objeto parcheable debe citarse así: @patch.object('my_app.models.FooClass', 'bar')
Lasma
2
@cavaunpeu: no (solo) parametrizado con cls, pero lo que es más importante (ya que selfy clsno significa nada especial en python), decorado con@classmethod
dwanderson

Respuestas:

67

Para agregar a la respuesta de Kit, especificar un tercer argumento patch.object()permite especificar el objeto / método simulado. De lo contrario, MagicMockse utiliza un objeto predeterminado .

    def fake_bar(self):
        print "Do something I want!"
        return True

    @patch.object(my_app.models.FooClass, 'bar', fake_bar)
    def test_enter_promotion(self):
        self.client.get(reverse(view))
        # Do something I want!

Tenga en cuenta que, si se especifica el objeto burla, el valor por defecto MagicMock()es no pasó en el objeto parcheado - por ejemplo, ya no:

def test_enter_promotion(self, mock_method):

pero en vez:

def test_enter_promotion(self):

http://www.voidspace.org.uk/python/mock/patch.html#patch-object

storm_m2138
fuente
Prefiero esta implementación. Es más claro, especialmente para los principiantes en pruebas unitarias.
Dorcioman
35

Ah, estaba confundido sobre dónde aplicar ese decorador de parches. Fijo:

class ViewsDoSomething(TestCase):
    view = 'my_app.views.do_something'

    @patch.object(my_app.models.FooClass, 'bar')
    def test_enter_promotion(self, mock_method):
        self.client.get(reverse(view))
Kit Sunde
fuente
20
¿Dónde se hace la conexión entre el método para simular y la implementación falsa ahora?
física
@physicalattraction, la conexión se realiza mediante el argumento mock_methodpasado a la función de prueba. He podido utilizar esta técnica en una de mis pruebas. Esto es útil cuando solo desea verificar si se llamó al método simulado.
Kalyan Vedala
@ rcode74: Cómo parchear un método de una instancia (algún otro objeto) dentro del método de prueba. por ejemplo: def my_method_to_be_proted (...): r = some_script.some_class (...); r.how_to_patch_this_method.
imsrgadich
1
@imsrgadich, harías algo como r.how_to_patch_this_method = MagicMock (). Puede consultar la documentación de MagicMock para ver cómo asignar un comportamiento al objeto simulado.
Kalyan Vedala