Mock vs MagicMock

138

Tengo entendido que MagicMock es un superconjunto de Mock que automáticamente hace "métodos mágicos", proporcionando así soporte para listas, iteraciones y demás ... Entonces, ¿cuál es la razón de la existencia de Mock ? ¿No es solo una versión simplificada de MagicMock que puede ignorarse prácticamente? ¿ La clase Mock conoce algún truco que no está disponible en MagicMock ?

Vladimir Ignatov
fuente

Respuestas:

99

¿Cuál es la razón de la existencia de Mock simple ?

El autor de Mock, Michael Foord, abordó una pregunta muy similar en Pycon 2011 (31:00) :

P: ¿Por qué MagicMock hizo algo diferente en lugar de simplemente doblar la habilidad en el objeto simulado predeterminado?

R: Una respuesta razonable es que la forma en que funciona MagicMock es que preconfigura todos estos métodos de protocolo creando nuevos Mocks y configurándolos, por lo que si cada simulacro nuevo crea un montón de simulacros nuevos y los establece como métodos de protocolo y luego todos esos protocolos Los métodos crearon un montón de simulacros más y los establecieron en sus métodos de protocolo, tienes una recursión infinita ...

¿Qué sucede si desea acceder a su simulacro como objeto contenedor como un error? ¿No quiere que eso funcione? Si cada simulación tiene automáticamente cada método de protocolo, entonces se vuelve mucho más difícil hacerlo. Y también, MagicMock hace algo de esta configuración previa para usted, estableciendo valores de retorno que pueden no ser apropiados, por lo que pensé que sería mejor tener esta conveniencia que tenga todo preconfigurado y disponible para usted, pero también puede tomar una simulación común objetar y simplemente configurar los métodos mágicos que desea que existan ...

La respuesta simple es: simplemente use MagicMock en todas partes si ese es el comportamiento que desea.

Ryne Everett
fuente
12
Creo que una mejor respuesta es: usa MagicMock si sabes lo que estás haciendo, de lo contrario usa Mock.
laike9m
56

Con Mock que puede burlarse de los métodos mágicos, pero hay que definirlos. MagicMock tiene "implementaciones predeterminadas de la mayoría de los métodos mágicos". .

Si no necesita probar ningún método mágico, Mock es adecuado y no trae muchas cosas extrañas a sus pruebas. Si necesita probar muchos métodos mágicos, MagicMock le ahorrará algo de tiempo.

Sean Redmond
fuente
Efectivamente, ya he leído la documentación. Eso no responde a mi pregunta: ¿por qué molestarse con Mock simple si MagicMock hace exactamente lo mismo y mucho más? No veo cosas extrañas en mis pruebas, solo uso el nombre diferente y listo. Entonces, ¿dónde está la trampa?
Vladimir Ignatov
39
Las pruebas deben ser mínimas y los objetos simulados deben ser mínimamente funcionales para que esté seguro exactamente de lo que está probando. Si usa MagicMock solo porque hace más pero no está probando explícitamente todo ese "más", corre el riesgo de que falle una prueba debido a un comportamiento predeterminado de MagicMock. Esta falla podría reflejar algo sobre los valores predeterminados de MagicMock más de lo que se supone que debe burlarse. Peor aún, corre el riesgo de que una prueba tenga éxito cuando debería haber fallado. El riesgo es pequeño, pero si esto sucede, perderá mucho tiempo.
Sean Redmond
1
Creo que es como usar JS simple contra Jquery. Claro, puede usar Jquery para hacer todo su JS, pero en algunos casos, solo quiere usar la herramienta mínima requerida para hacer el trabajo. Creo que esos casos suelen ser extremadamente simples o extremadamente complejos.
acurruca el
49

Para empezar, MagicMockes una subclase de Mock.

class MagicMock(MagicMixin, Mock)

Como resultado, MagicMock proporciona todo lo que proporciona Mock y más. En lugar de pensar en Mock como una versión simplificada de MagicMock, piense en MagicMock como una versión extendida de Mock. Esto debería responder a sus preguntas sobre por qué existe Mock y qué proporciona Mock además de MagicMock.

En segundo lugar, MagicMock proporciona implementaciones predeterminadas de muchos / la mayoría de los métodos mágicos, mientras que Mock no. Consulte aquí para obtener más información sobre los métodos mágicos proporcionados.

Algunos ejemplos de métodos mágicos proporcionados:

>>> int(Mock())
TypeError: int() argument must be a string or a number, not 'Mock'
>>> int(MagicMock())
1
>>> len(Mock())
TypeError: object of type 'Mock' has no len()
>>> len(MagicMock())
0

Y estos que pueden no ser tan intuitivos (al menos no intuitivos para mí):

>>> with MagicMock():
...     print 'hello world'
...
hello world
>>> MagicMock()[1]
<MagicMock name='mock.__getitem__()' id='4385349968'>

Puede "ver" los métodos agregados a MagicMock a medida que se invocan por primera vez:

>>> magic1 = MagicMock()
>>> dir(magic1)
['assert_any_call', 'assert_called_once_with', ...]
>>> int(magic1)
1
>>> dir(magic1)
['__int__', 'assert_any_call', 'assert_called_once_with', ...]
>>> len(magic1)
0
>>> dir(magic1)
['__int__', '__len__', 'assert_any_call', 'assert_called_once_with', ...]

Entonces, ¿por qué no usar MagicMock todo el tiempo?

La pregunta para usted es: ¿Está de acuerdo con las implementaciones del método mágico por defecto? Por ejemplo, ¿está bien no cometer mocked_object[1]errores? ¿Estás de acuerdo con las consecuencias imprevistas debido a que las implementaciones del método mágico ya están allí?

Si la respuesta a estas preguntas es sí, entonces siga adelante y use MagicMock. De lo contrario, quédate con Mock.

usuario650654
fuente
12

Esto es lo que dice la documentación oficial de Python :

En la mayoría de estos ejemplos, las clases Mock y MagicMock son intercambiables. Como MagicMock es la clase más capaz, es una clase sensata para usar por defecto.

usuario2916464
fuente
3

He encontrado otro caso particular en el que simple Mock puede ser más útil que MagicMock:

In [1]: from unittest.mock import Mock, MagicMock, ANY
In [2]: mock = Mock()
In [3]: magic = MagicMock()
In [4]: mock.foo == ANY
Out[4]: True
In [5]: magic.foo == ANY
Out[5]: False

Comparar contra ANYpuede ser útil, por ejemplo, comparar casi todas las claves entre dos diccionarios en los que se calcula algún valor utilizando un simulacro.

Esto será válido si está utilizando Mock:


self.assertDictEqual(my_dict, {
  'hello': 'world',
  'another': ANY
})

mientras aumentará un AssertionErrorsi has usadoMagicMock

Manu
fuente