- Introducción
- unittest.,mock or mock
- Decorator
- Resource location
- mock return_value vs side_effect
- Mock Anided Calls
- Verify Exceptions
- Clearing lru_cache
- Mock Module Level/Global Variables
- Mock Instance Method
- Mock Class Method
- Mock Entire Class
- Mock Async Calls
- mock instance types
- mock BUILTIN
open
function - conclusion
introduction
burlarse de los recursos al escribir pruebas en Python puede ser confuso si no está familiarizado con hacer tales cosas., En este post voy a cubrir varios aspectos del código de burla, que espero sea un recurso útil para aquellos que están un poco atascados.
nota: en los ejemplos de código Estoy usando pytest, pero en su mayor parte eso no debería importar.
unittest.mock or mock
para ‘mockear’ un recurso primero necesitaremos el módulo mock
, y este es nuestro primer obstáculo: ¿qué versión necesitamos? es decir, hay dos y ambos parecen ser oficiales (mock
y unittest.mock
).,
el módulo mock
es una biblioteca compatible con versiones anteriores que puedes descargar desde PyPy, donde as unittest.mock
es lo mismo pero solo compatible con la versión de Python que estás usando.,
por lo tanto, en casi todos los casos, querrá importarlo de la siguiente manera:
import unittest.mock as mock
para obtener más ejemplos, consulte esta guía de referencia
Decorator
mock Resources es usar un decorador Python alrededor de su función de prueba:
@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123
en este caso, lo que estamos parcheando (thing
) puede ser una variable o una función.,
Cuando hagas esto tendrás que pasar un argumento a tu función (puedes nombrarlo como quieras †) que será un MagicMock
.
esto significa que si no hace nada más, entonces las llamadas a thing
darán como resultado (al menos en el ejemplo anterior) que se devuelva el valor 123
.
† convención es el nombre de la variable
mock_<noun>
.,
Si se está burlando de varias cosas, apilará los decoradores de simulación uno encima del otro y los pasará a la función de prueba:
@mock.patch("third")@mock.patch("second")@mock.patch("first")def test_stuff(mock_first, mock_second, mock_third): ...
ubicación del recurso
es importante saber que al burlarse debe especificar la ubicación del recurso a donde se importa., Esto se explica mejor a modo de ejemplo
Imagine que tengo un móduloapp.foo
y dentro de ese módulo importo otra dependencia como así:
from app.bar import thing
podría pensar que cuando llama amock.patch
que le pasa una referencia al recurso como app.bar.thing
. Eso solo sería relevante si el recurso estaba siendo llamado con esa ruta completa dentro del módulo app.foo
(por ejemplo, if app.foo
called app.bar.thing(...)
).,
si no se hace referencia a la ruta completa del espacio de nombres, que no está en el ejemplo anterior (observe que importamos solo el recurso thing
). Significa que necesitamos especificar el espacio de nombres de referencia para simular como donde se importa:
@mock.patch('app.foo.thing')
así que aunque thing
existe dentro de app.bar
especificamos app.foo.thing
como app.foo
es donde lo hemos importado para su uso. Esto atrapa a la gente todo el tiempo.,
Mock return_value vs side_effect
Si su función tiene un try/except alrededor de ella, entonces puede usar side_effect
para hacer que la llamada de la función Active una excepción como el valor devuelto:
@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))
nota: si hubiera utilizado
return_value=Exception('whoops')
entonces el mock devolvería la representación de cadena de la excepción en lugar de levantar una excepción como lo haceside_effect
.,se llamó al método en un objeto simulado:la razón por la que esto se puede complicar es debido a cómo un mock devolverá un nuevo mock al acceder a una propiedad en un mock:
el código anterior se equivocará:
AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)
deberá asegurarse de que afirma el mock en el momento correcto:
verificar excepciones
Si queremos verificar que alguna pieza de código lanza un tipo
Exception
cuando lo necesitamos podemos simular recursos específicos para lanzar una excepción y luego usarpytest.raises
como un administrador de contexto alrededor del llamante de nuestro código para ser verificado.,podemos capturar y hacer aserciones contra este comportamiento esperado burlando primero el recurso que queremos lanzar una excepción y conseguir que lance nuestra propia excepción falsa usando el parámetro
side_effect
.lo Siguiente que especifica exactamente el tipo de excepción estamos esperando que se planteó la utilización de
pytest.raises(T)
:Nota: no cometa el error de poner cualquier afirmación en el
with
context manager., Una vez que la excepción es levantada por la función que es llamada dentro del gestor de contextowith
, todo el código después de ella dentro del bloque es omitido.Clearing lru_cache
Si una función que desea probar tiene el
functools.lru_cache
decorator aplicado, entonces deberá ser consciente de burlarse de la respuesta de esa función, ya que se almacenará en caché en una prueba y el resultado almacenado en caché se devolverá cuando llame a la función nuevamente para probar algún otro comportamiento (y podría es probable que te confunda cuando veas la respuesta inesperada).,Para solucionar este problema es muy fácil, porque
lru_cache
proporciona funciones adicionales cuando decoratoring sus funciones, contiene:
cache_info
cache_clear
El último (
cache_clear
), es lo que sería necesario llamar. Esto se demuestra a continuación:Nota: depurar esto no siempre es obvio., Más tarde demostré cómo simular la función builtin
open
, y en ese escenario me topé con este problema, porque aunque no me estaba burlando de la función de nivel superior en sí (me estaba burlando de la llamada aopen
dentro), el contenido del archivo que se abría era lo que se devolvía y se almacenaba en caché.Mock Module Level/Global Variables
con una variable de módulo puede establecer el valor directamente o usar
mock.patch
.,En el siguiente ejemplo tenemos la variable
client_id
que es una variable global dentro de la etiquetaapp.aws
módulo que vamos a importar a referencia en otras partes de nuestro código:En el
mock.patch
ejemplo, hay dos cosas a considerar:
- no usamos
return_value
.- no se pasa ninguna instancia simulada a la función de prueba.,
esto se debe a que estamos modificando una variable y no una función directa o’ callable’, por lo que no hay necesidad de pasar un simulacro a la función de prueba (si desea cambiar el valor unas cuantas veces dentro de la prueba en sí, entonces se burlaría de la variable pero no asignaría inmediatamente un valor en el decorador).
Mock Instance Method
Hay varias maneras de lograr la simulación de un método de instancia., Un enfoque común es usar
mock.patch.object
, así:otro enfoque es burlarse del método como lo haría con una función normal, pero hace referencia al método a través del nombre de clase:
otro enfoque (aunque más pesado) para burlarse de un método de instancia de clase es aprovechar el hecho de que un Mock devolverá una nueva instancia mock cuando se llame:
@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"
nota: en el ejemplo anterior nos burlamos de toda la clase, que podría no ser lo que quieres. Si no, utilice el ejemplo anterior
mock.patch.object
en su lugar.,la razón por la que el ejemplo anterior funciona es porque estamos configurando
return_value
en nuestro mock. Debido a que este es unMagicMock
cada atributo referenciado devuelve una nueva instancia mock (una función o propiedad que llame en un mock no tiene que existir) y por lo tanto llamamosmade_up_function
en el mock devuelto, y en ese mock recién creado establecemos el finalreturn_value
a123
.,pero como se mencionó en la nota anterior, este enfoque podría ser un poco demasiado brusco dependiendo de cuáles sean sus necesidades (ya sea que le importe si tiene una clase funcional o no).
Mock Class Method
para simular un método de clase es un enfoque similar a la burla de un método de instancia.,
un enfoque podría ser que te burlas de toda la clase (pero ahora tienes una menos
return_value
para asignar a):mock_class.ClassMethodName.return_value = "123"
o mejor aún, deberías burlarte de ella como lo harías con cualquier función normal, pero solo haz referencia al método a través de la clase:
simula toda la clase
class necesitarás establecer el
return_value
para que sea una nueva instancia de la clase.,vea otros consejos de burla relacionados con la clase aquí
llamadas asíncronas Mock
burlarse del código asíncrono es probablemente el aspecto más confuso de la burla. Mi’ ir a ‘ solución voy a explicar primero, pero después de que voy a compartir algunos métodos alternativos que he visto y probado en el pasado.,
primero considere este código asincrónico dentro de un módulo
app.foo
:import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)
si necesitamos simular la corrutina
app.stuff.some_concurrent_function
, entonces podemos resolver esto creando una función que actúe como una corrutina y puede configurarse para diferentes tipos de respuestas:nota: el ejemplo utiliza tornado para ejecutar una prueba asíncrona.,e alternatives
AsyncMock
Nota: Esto utiliza el paquete
pytest-asyncio
para ayudar con las pruebas de código asynciocomencemos con el código p>
import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y
ahora aquí es cómo nos burlaríamos de él
Monkey Patch
subclase magicmock
función inline async
tipos de instancias mock
al burlarse de un objeto, encontrará que el simulacro reemplaza todo el objeto y, por lo tanto, puede causar que las pruebas pasen (o fallen) de maneras inesperadas.,
Es decir, si necesita hacer un mock más como la interfaz concreta, entonces hay dos maneras de hacerlo:
spec
wrap
Podemos usar mock’s
spec
Característica para imitar todos los métodos / atributos del objeto que se está burlando. Esto asegura que sus mock tengan la misma api que los objetos que están reemplazando.Nota: hay más estrictas
spec_set
que se producirá unAttributeError
.,esto se demuestra mejor con un ejemplo:
el parámetro
wrap
por otro lado le permite ‘espiar’ la implementación, así como afectar su comportamiento.,en uno de los anteriores que se burla de todo el objeto, no solo de un único método:mock builtin open function
la biblioteca mock de Python proporciona una abstracción para burlarse de la función builtin
open
function a lot simpler simplerThe
create=True
param set onmock.patch
significa que elmock.MagicMock
devuelto creará automáticamente cualquier atributo que se invoque en el mock (esto se debe a que la funciónopen
intentará acceder a muchas cosas diferentes y es más fácil para mock burlarse de todo eso para usted).,conclusión
ahí terminaremos. Esperemos que esta lista de técnicas de burla será capaz de ver a través de incluso el más complejo de código que necesita ser probado. Déjame saber lo que piensas en twitter.