Python burla no es algo intuitivo acerca de usted. Usted no es fácil de conseguir a los apretones con y toma varios » Ah, ahora lo entiendo!»momentos antes de que te entiendan mejor., Usted es una herramienta poderosa y fantástica para habilitar pruebas unitarias automatizadas, pero conozco a muchos desarrolladores que «sobreviven» con el conocimiento que tienen y escribir esas pruebas lleva más tiempo de lo que debería.
Hay muchos artículos sobre burlarse, y sí, Ahora estoy escribiendo otro. Sin embargo, realmente quiero tener una oportunidad de explicarlo claramente, especialmente aquellos » Aha!»momentos que impulsaron mi comprensión.
En primer lugar, ¿qué es la burla? Aquí hay una cita de los documentos:
mock es una biblioteca para probar en Python., Le permite reemplazar partes de su sistema bajo prueba con objetos simulados y hacer afirmaciones sobre cómo se han utilizado.
Así que «permite reemplazar partes de sus sistemas», pero ¿qué partes? Resulta que usted puede burlarse de casi cualquier cosa puede definir, tales como:
- funciones
- clases
- objetos
a reemplazarlos por «burlarse» objetos que son instancias de la Maqueta o MagicMock clase.,
luego «hacer aserciones» sobre esa instancia simulada, que es una forma de verificar que la instancia simulada se usó de la manera que esperaba.
en este artículo voy a cubrir los conceptos básicos de la sustitución de partes de sus sistemas, pero no voy a cubrir hacer afirmaciones.
comencemos con un ejemplo simple. Voy a crear un nuevo módulo llamado simple.py y en ese módulo voy a definir una función llamada simple_function que sólo devuelve una cadena así:
def simple_function():
return "You have called simple_function"
Ahora voy a crear un segundo módulo llamado use_simple.py., En el módulo que voy a importar el simulacro de paquete de unittest e importar el módulo sencillo:
from unittest import mockimport simple
a continuación voy a escribir dos funciones, una que llama simple_function normalmente y que se burla de la llamada a ella. Tenga en cuenta que normalmente haría esto en un contexto de prueba, pero estoy Desmontando a propósito para que pueda enfocarme en la parte burlona. La primera función:
def use_simple_function():
result = simple.simple_function()
print(result)use_simple_function()
Como puede ver, es sólo imprime el resultado de simple_function., Específicamente, la salida es:
You have called simple_function
para la segunda función, para simular simple_function voy a usar la mock.decorador de parches. Este decorador le permite especificar lo que desea burlarse pasándole una cadena en el paquete format’.módulo.FunctionName’., En nuestro ejemplo no hay paquete, pero el módulo se llama simple y la función se llama simple_function, por lo que el decorador se verá como:
@mock.patch('simple.simple_function')
luego tenemos que escribir nuestra segunda función como:
@mock.patch('simple.simple_function')
def mock_simple_function():
la he llamado mock_simple_function, pero se puede llamar cualquier cosa. Nos falta un parámetro aquí porque cuando decoras una función con @mock.patch It pasará una instancia de la clase MagicMock (un objeto MagicMock) que se utiliza para reemplazar la función que se está burlando., Así que se verá así:
@mock.patch('simple.simple_function')
def mock_simple_function(mock_simple_func):
así que especificando @mock.patch (‘simple.simple_function’) estoy diciendo que quiero reemplazar simple_function con el objeto MagicMock asignado al parámetro llamado mock_simple_func.
inicialmente vamos a desarrollar la función imprimiendo mock_simple_func y luego llamarla:
Cuando se ejecuta, la salida es:
<MagicMock name='simple_function'>
que es un objeto MagicMock que se llamará en lugar de simple_function., Es importante tener en cuenta que el atributo name del objeto es la cosa que se está burlando, y el id es un número único para el objeto.
si ahora también imprimimos simple_function (pero no lo llamamos):
que produce la salida:
<MagicMock name='simple_function'>
<MagicMock name='simple_function'>
puede ver que de los ids coincidentes que mock_simple_func es el mismo objeto MagicMock que simple_function.
hasta ahora todo bien. Nos hemos burlado de simple_function. Para ser explícito, simple_function ha sido burlado porque ha sido reemplazado por un objeto MagicMock.,
agreguemos las líneas de la primera función:
cuando se ejecuta, la salida es:
Como se puede ver en la tercera línea de salida cuando simple_function se llama realmente crea un objeto MagicMock diferente. Recuerde que ya que nos hemos burlado de simple_function, en realidad es un objeto MagicMock, por lo que cuando llamamos simple_function en realidad estamos llamando al objeto MagicMock!
para mí, aquí es donde se establece la confusión.
- ¿Por qué se crea un nuevo objeto MagicMock?
- ¿y cómo diablos puedes llamar a un objeto de todos modos?, Normalmente obtienes «TypeError:’ * * * * ‘object is not callable» cuando lo intentas.
hagamos una pausa por un minuto para mirar más de cerca a MagicMock.
MagicMock tiene magia en su nombre porque tiene implementaciones predeterminadas de la mayoría de los métodos de python magic. ¿Qué es un método mágico de python? Son todas las funciones especiales de python que tienen doble subrayado al principio y al final de su nombre. Puede encontrar una lista de ellos aquí. El de interés aquí es _ _ call__., La función de llamada se describe como:
llamada cuando se llama a un objeto como función
así que si una clase implementa esta función, puede crear una instancia de la clase, y luego llamar a esa instancia como una función, es decir :
Por lo tanto, cuando se llama a MagicMock como función, el __se llama a la función Call__. La implementación de MagicMock de esta función crea un nuevo simulacro!
así que cuando llamamos a simple_function () se crea y devuelve un nuevo mock. No solo eso, sino que tenemos el concepto de padre e hijo se burla., Cuando un simulacro crea otro, se convierte en el padre y el recién creado en el hijo. Esa burla infantil podría crear otras burlas para terminar con una reliquia de objetos simulados.
punto de aprendizaje 1: Cuando un objeto MagicMock es llamado como una función, por defecto crea y devuelve un nuevo objeto MagicMock.
de vuelta a nuestro ejemplo. Hasta ahora solo nos hemos burlado de simple_function pero no hemos hecho nada con él que no sea imprimir los MagicMocks que se crean como resultado. Digamos que quería cambiar lo que fue devuelto desde simple_function (), ¿cómo lo haría?, Lo haría usando la propiedad return_value de MagicMock así (también voy a eliminar las dos primeras impresiones ya que ya no son necesarias):
Cuando se ejecuta, la salida es:
You have mocked simple_function
Como puedes ver cuando simple_function() ahora se llama, se devuelve MagicMock return_value en lugar de crear un segundo objeto MagicMock.
punto de Aprendizaje 2: para cambiar lo que se devuelve cuando se llama a un objeto MagicMock como una función, establezca return_value.
si desea hacer algo más que cambiar el valor devuelto, puede usar MagicMock.side_effect característica., Le permite especificar una función completamente nueva que se llamará en lugar de la que se está burlando. Hagámoslo. En primer lugar, definiré una nueva función que será el side_effect (tenga en cuenta que necesita tener el mismo parámetro establecido que la función que se está burlando):
def side_effect_function():
return "SKABLAM!"
ahora podemos definir una nueva función que utiliza este efecto secundario de la siguiente manera:
Todo es lo mismo que el mock_simple_func más adelante excepto que estoy configurando el side_effect de mock_simple_func en lugar de return_value.,
la salida es:
SKABLAM!
Un buen caso de uso para usar efectos secundarios es si desea probar un flujo de error y, por lo tanto, desea generar una excepción en su prueba. Cambiaré la función side_effect_function para generar un error:
def side_effect_function():
raise FloatingPointError("A disastrous floating point error has occurred")
ahora la salida es (eliminé parte del seguimiento para mayor claridad):
Traceback (most recent call last):
File "use_simple.py”, line 18, in side_effect_function
raise FloatingPointError("A disastrous floating point error has occurred”)
FloatingPointError: A disastrous floating point error has occurred
punto de aprendizaje 3: para hacer más que cambiar el valor de retorno de un MagicMock, establezca side_effect.
burlarse de clases
hasta ahora nos hemos burlado de una función, pero ¿qué tal una clase?, Definamos una clase en el simple.py llamado SimpleClass con un método llamado explode:
class SimpleClass(object):
def explode(self):
return "KABOOM!"
ahora en use_simple.py vamos a escribir una función que utiliza SimpleClass y luego llamar a esa función:
def use_simple_class():
inst = simple.SimpleClass()
print(inst.explode())use_simple_class()
Cuando se ejecuta la salida es:
KABOOM!
ahora vamos a definir una segunda función y elegir simular la totalidad de simple_class usando el @mock.decorador de parches:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
como al burlarse de una función, el @ mock.,patch decorator pasa un objeto MagicMock que reemplaza la clase que se está burlando en la función que está decorando. El objeto MagicMock en este caso se asigna al argumento mock_class.
por el momento solo imprimiré el objeto MagicMock y luego ejecutaré la función:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)mock_simple_class()
Cuando ejecute la salida es:
<MagicMock name='SimpleClass'>
Al igual que con la burla de una función, se crea y se pasa un objeto MagicMock., Ahora vamos a imprimir SimpleClass para que pueda ver que se ha burlado y reemplazado con el mismo objeto MagicMock:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)
print(simple.SimpleClass)
la salida ahora es:
<MagicMock name='SimpleClass'>
<MagicMock name='SimpleClass'>
vamos a crear una instancia de SimpleClass, es decir, llamar a SimpleClass (), y luego imprimirlo. Recuerde, ya que nos hemos burlado de SimpleClass, lo que realmente sucederá es que llamaremos al objeto MagicMock como una función:
la salida es:
así que como se esperaba, llamar a SimpleClass() y por lo tanto llamar al objeto MagicMock como una función crea un nuevo objeto MagicMock.,
hasta ahora, burlarse de una clase ha sido muy parecido a burlarse de una función. Sin embargo, en realidad cuando definimos una clase entonces crearemos objetos usando esa clase y son esos objetos la mayoría de las veces que realmente queremos burlarnos. La pregunta es, ¿cómo burlarse de una instancia creada a partir de una clase? Bueno, cuando te burlas de una clase y consecuentemente se crea un objeto MagicMock, si te refieres a return_value en ese objeto MagicMock crea un nuevo objeto MagicMock que representa la instancia de la clase creada. ¡Caray! ¡Eso fue una frase y media! Este fue definitivamente uno de mis » Aha!,»momentos, pero incluso escribirlo no lo deja claro. Vamos a imprimir return_value para que pueda ver lo que quiero decir:
la salida es:
la tercera línea es de imprimir la instancia de la clase creada, y la cuarta línea es de imprimir el return_value del objeto MagicMock pasado a la función. Como puede ver, se refieren al mismo objeto MagicMock.
punto de Aprendizaje 4: Cuando te burlas de una clase, se crea un objeto MagicMock. Cuando se crea una instancia de esa clase se crea un nuevo objeto MagicMock., Cuando se hace referencia al return_value del objeto MagicMock de esa clase, se obtiene el mismo objeto MagicMock que se creó cuando se creó la instancia de esa clase.
«Ahhhhh, ¿por qué es eso importante?!?!?»¡Te oigo llorar!
«Es importante si quieres burlarte de la función de explode» a continuación a cambio!
la siguiente pregunta que debe hacer es: ¿el método que quiero simular es un método de clase o un método de instancia? Porque la forma de cambiar el return_value del método será diferente dependiendo de la respuesta.,
Si el método es un método de clase, a continuación, establezca la return_value así:
MagicMockObject.ClassMethodName.return_value = "A delightful return value"
Pero, si era un método de instancia que haría:
MagicMockObject.return_value.InstanceMethodName.return_value = "A daring return value"
Con esto en mente vamos a cambiar el return_value de explotar, que es un método de instancia (voy a quitar la mayoría de las instrucciones de impresión para mayor claridad) :
En el código anterior, mock_class.return_value devuelve el objeto MagicMock que representa la instancia de SimpleClass.
mock_class.return_value.,explode devuelve el objeto MagicMock que representa el método explode de la instancia de SimpleClass.
por lo tanto establecer el return_value de mock_class.return_value.explode establece el valor de retorno del método explode de la instancia de SimpleClass.
la salida es:
BOO!
Al igual que cuando se burla de una función, también puede establecer el side_effect para Métodos de clases y objetos.
espero que este artículo te haya ayudado a entender la burla un poco más. Por favor, recomiendo si te gustó y no dude en dejar una respuesta Como me encantaría saber de usted.