"unittest.mock" --- primeros pasos
**********************************

Nuevo en la versión 3.3.


Usando Mock
===========


Métodos de parcheo Mock
-----------------------

Usos comunes para objetos "Mock" incluye:

* Métodos de parcheo

* Métodos de grabación de llamadas sobre objetos

Es posible que desee reemplazar un método en un objeto para comprobar
que se llama con los argumentos correctos por otra parte del sistema:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')
<MagicMock name='method()' id='...'>

Una vez que se ha utilizado nuestro mock ("real.method" en este
ejemplo) tiene métodos y atributos que le permiten hacer afirmaciones
sobre cómo se ha utilizado.

Nota:

  En la mayoría de estos ejemplos, las clases "Mock" y "MagicMock" son
  intercambiables. Como el "MagicMock" es la clase más capaz, hace que
  sea sensato usarlo por defecto.

Una vez que el mock ha sido llamado su atributo "called" se establece
en "True". Lo que es más importante, podemos usar el método
"assert_called_with()" o "assert_called_once_with()" para comprobar
que se llamó con los argumentos correctos.

En este ejemplo se prueba que llamar a "ProductionClass().method" da
como resultado una llamada al método "something":

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)


Mock de llamadas a métodos sobre un objeto
------------------------------------------

En el último ejemplo, parcheamos un método directamente en un objeto
para comprobar que se llamó correctamente. Otro caso de uso común es
pasar un objeto a un método (o a alguna parte del sistema sometido a
prueba) y, a continuación, comprobar que se utiliza de la manera
correcta.

La sencilla "ProductionClass" a continuación tiene un método "closer".
Si se llama con un objeto, entonces llama a "close" en él.

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...

Así que para probarlo necesitamos pasar un objeto con un método
"close" y comprobar que se llamó correctamente.

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()

No tenemos que hacer ningún trabajo para proporcionar el método de
'close' en nuestro mock. El acceso al cierre lo crea. Por lo tanto, si
'close' aún no se ha llamado, entonces acceder a él en la prueba lo
creará, pero "assert_called_with()" generará una excepción de error.


Clases Mocking
--------------

Un caso de uso común es simular las clases que crea la instancia del
código que se está probando. Cuando se aplica un parche a una clase,
esa clase se reemplaza por un mock. Las instancias se crean *llamando
a la clase*. Esto significa que tiene acceso a la "instancia mock"
examinando el valor devuelto de la clase simulada.

En el ejemplo siguiente tenemos una función "some_function" que crea
una instancia de "Foo" y llama a un método en él. La llamada a
"patch()" reemplaza la clase "Foo" con un mock. La instancia "Foo" es
el resultado de llamar al mock, por lo que se configura modificando el
mock "-Mock.return_value".

   >>> def some_function():
   ...     instance = module.Foo()
   ...     return instance.method()
   ...
   >>> with patch('module.Foo') as mock:
   ...     instance = mock.return_value
   ...     instance.method.return_value = 'the result'
   ...     result = some_function()
   ...     assert result == 'the result'


Nombrando tus mocks
-------------------

Puede ser útil poner un nombre a tus mocks. El nombre se muestra en la
reproducción del mock y puede ser útil cuando el mock aparece en los
mensajes de error de la prueba. El nombre también se propaga a los
atributos o métodos del mock:

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>


Siguiendo todas las llamadas
----------------------------

A menudo, desea realizar un seguimiento de más de una llamada a un
método. El atributo "mock_calls" registra todas las llamadas a los
atributos secundarios del mock, y también a sus hijos.

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]

Si realiza una afirmación sobre "mock_calls" y se ha llamado a
cualquier método inesperado, la aserción fallará. Esto es útil porque
además de afirmar que se han realizado las llamadas que esperaba,
también está comprobando que se hicieron en el orden correcto y sin
llamadas adicionales:

Utiliza el objeto "call" para construir listas y compararlas con
"mock_calls":

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True

Sin embargo, los parámetros de las llamadas que devuelven mocks no se
registran, lo que significa que no es posible realizar un seguimiento
de las llamadas anidadas donde los parámetros utilizados para crear
ancestros son importantes:

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True


Establecer valores de retorno y atributos
-----------------------------------------

Establecer los valores de retorno en un objeto mock es sumamente
fácil:

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3

Por supuesto, puede hacer lo mismo con los métodos en el mock:

>>> mock = Mock()
>>> mock.method.return_value = 3
>>> mock.method()
3

El valor devuelto también se puede establecer en el constructor:

>>> mock = Mock(return_value=3)
>>> mock()
3

Si necesitas una configuración de atributo en su mock, simplemente
haga:

>>> mock = Mock()
>>> mock.x = 3
>>> mock.x
3

A veces desea simular una situación más compleja, como por ejemplo
"mock.connection.cursor().execute("SELECT 1")". Si esperamos que esta
llamada devuelva una lista, entonces tenemos que configurar el
resultado de la llamada anidada.

Podemos usar "call" para construir el conjunto de llamadas en una
"llamada encadenada" como esta para una fácil afirmación después:

>>> mock = Mock()
>>> cursor = mock.connection.cursor.return_value
>>> cursor.execute.return_value = ['foo']
>>> mock.connection.cursor().execute("SELECT 1")
['foo']
>>> expected = call.connection.cursor().execute("SELECT 1").call_list()
>>> mock.mock_calls
[call.connection.cursor(), call.connection.cursor().execute('SELECT 1')]
>>> mock.mock_calls == expected
True

Es la llamada a ".call_list()" la que convierte nuestro objeto de
llamada en una lista de llamadas que representan las llamadas
encadenadas.


Generar excepciones con mocks
-----------------------------

Un atributo útil es "side_effect". Si establece esto en una clase o
instancia de excepción, se producirá la excepción cuando se llame al
mock.

>>> mock = Mock(side_effect=Exception('Boom!'))
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!


Funciones de efectos secundarios e iterables
--------------------------------------------

"side_effect" también se puede asignar en una función o en una
iterable. El caso de uso para "side_effect" como un iterable es donde
se va a llamar a su mock varias veces, y desea que cada llamada
devuelva un valor diferente. Cuando se asigna "side_effect" en un
iterable cada llamada al mock devuelve el siguiente valor de lo
iterable:

>>> mock = MagicMock(side_effect=[4, 5, 6])
>>> mock()
4
>>> mock()
5
>>> mock()
6

Para casos de uso más avanzados, como variar dinámicamente los valores
devueltos en función de cómo se llame al mock, "side_effect" puede ser
una función. Se llamará a la función con los mismos argumentos que el
mock. Lo que sea que la función devuelve es lo que devuelve la
llamada:

>>> vals = {(1, 2): 1, (2, 3): 2}
>>> def side_effect(*args):
...     return vals[args]
...
>>> mock = MagicMock(side_effect=side_effect)
>>> mock(1, 2)
1
>>> mock(2, 3)
2


Iteradores asincrónicos de Mocking
----------------------------------

Desde Python 3.8, "AsyncMock" y "MagicMock" tienen soporte para mock
Iteradores asíncronos through "__aiter__". El "return_value" atributo
de "__aiter__" puede ser usado para asignar los valores de retorno que
podrían ser usados por iteración.

>>> mock = MagicMock()  # AsyncMock also works here
>>> mock.__aiter__.return_value = [1, 2, 3]
>>> async def main():
...     return [i async for i in mock]
...
>>> asyncio.run(main())
[1, 2, 3]


El gestor de contexto asincrónico de Mocking
--------------------------------------------

Desde Python 3.8, "AsyncMock" y "MagicMock" tienen soporte para mock
Gestores de Contexto Asíncronos a través de "__aenter__" y
"__aexit__". De forma predeterminada, las instancias "__aenter__" y
"__aexit__" son instancias de "AsyncMock" que devuelven una función
asincrónica.

>>> class AsyncContextManager:
...     async def __aenter__(self):
...         return self
...     async def __aexit__(self, exc_type, exc, tb):
...         pass
...
>>> mock_instance = MagicMock(AsyncContextManager())  # AsyncMock also works here
>>> async def main():
...     async with mock_instance as result:
...         pass
...
>>> asyncio.run(main())
>>> mock_instance.__aenter__.assert_awaited_once()
>>> mock_instance.__aexit__.assert_awaited_once()


Creando un mock desde un objeto existente
-----------------------------------------

Un problema con el uso excesivo de mocking es que combina sus pruebas
a la implementación de sus mocks en lugar de su código real.
Supongamos que tiene una clase que implementa "some_method". En una
prueba para otra clase, se proporciona un mock de este objeto que
*also* proporciona "some_method". Si más tarde refactoriza la primera
clase, para que ya no tenga "some_method" - entonces sus pruebas
seguirán pasando a pesar de que su código está ahora roto!

"Mock" le permite proporcionar un objeto como especificación para el
mock, utilizando el argumento de palabra clave *spec*. El acceso a
métodos / atributos en el mock que no existen en el objeto de
especificación generará inmediatamente un error de atributo. Si cambia
la implementación de la especificación, las pruebas que usan esa clase
comenzarán a fallar inmediatamente sin tener que crear instancias de
la clase en esas pruebas.

>>> mock = Mock(spec=SomeClass)
>>> mock.old_method()
Traceback (most recent call last):
   ...
AttributeError: object has no attribute 'old_method'

El uso de una especificación también permite una coincidencia más
inteligente de las llamadas realizadas al mock, independientemente de
si algunos parámetros se pasaron como argumentos posicionales o con
nombre:

   >>> def f(a, b, c): pass
   ...
   >>> mock = Mock(spec=f)
   >>> mock(1, 2, 3)
   <Mock name='mock()' id='140161580456576'>
   >>> mock.assert_called_with(a=1, b=2, c=3)

Si desea que esta coincidencia más inteligente también funcione con
llamadas de método en el mock, puede usar auto-speccing.

Si desea una forma más fuerte de especificación que impida la
configuración de atributos arbitrarios, así como la obtención de
ellos, entonces puede usar *spec_set* en lugar de *spec*.


Decoradores de Parches
======================

Nota:

  Con "patch()" importa que parchee objetos en el espacio de nombres
  donde se buscan. Esto es normalmente sencillo, pero para una guía
  rápida lea where to patch.

Una necesidad común en las pruebas es aplicar revisiones a un atributo
de clase o a un atributo de módulo, por ejemplo, aplicar revisiones a
una clase integrada o parchear una clase en un módulo para probar que
se crea una instancia. Los módulos y las clases son efectivamente
globales, por lo que el parcheo en ellos tiene que deshacerse después
de la prueba o el parche persistirá en otras pruebas y causará
problemas difíciles de diagnosticar.

mock proporciona tres decoradores convenientes para esto: "patch()",
"patch.object()" y "patch.dict()". "patch" toma una sola cadena del
formulario "package.module.Class.attribute" para especificar el
atributo que está parcheando. También, opcionalmente, toma un valor
con el que desea que se reemplace el atributo (o clase o lo que sea).
'patch.object' toma un objeto y el nombre del atributo con el que
desea parchear, además, opcionalmente, del valor con el que parcharlo.

"patch.object":

   >>> original = SomeClass.attribute
   >>> @patch.object(SomeClass, 'attribute', sentinel.attribute)
   ... def test():
   ...     assert SomeClass.attribute == sentinel.attribute
   ...
   >>> test()
   >>> assert SomeClass.attribute == original

   >>> @patch('package.module.attribute', sentinel.attribute)
   ... def test():
   ...     from package.module import attribute
   ...     assert attribute is sentinel.attribute
   ...
   >>> test()

Si está parcheando un módulo (incluyendo "builtins") entonces use
"patch()" en lugar de "patch.object()":

>>> mock = MagicMock(return_value=sentinel.file_handle)
>>> with patch('builtins.open', mock):
...     handle = open('filename', 'r')
...
>>> mock.assert_called_with('filename', 'r')
>>> assert handle == sentinel.file_handle, "incorrect file handle returned"

EL nombre del módulo puede ser 'dotted', en el formulario
"package.module" si es necesario:

   >>> @patch('package.module.ClassName.attribute', sentinel.attribute)
   ... def test():
   ...     from package.module import ClassName
   ...     assert ClassName.attribute == sentinel.attribute
   ...
   >>> test()

Un buen patrón en realidad es decorar los métodos de pruebas propios:

>>> class MyTest(unittest.TestCase):
...     @patch.object(SomeClass, 'attribute', sentinel.attribute)
...     def test_something(self):
...         self.assertEqual(SomeClass.attribute, sentinel.attribute)
...
>>> original = SomeClass.attribute
>>> MyTest('test_something').test_something()
>>> assert SomeClass.attribute == original

Si desea parchear con un Mock, puede usar "patch()" con un solo
argumento (o "patch.object()" con dos argumentos). El mock se creará
para usted y se pasará a la función de prueba / método:

>>> class MyTest(unittest.TestCase):
...     @patch.object(SomeClass, 'static_method')
...     def test_something(self, mock_method):
...         SomeClass.static_method()
...         mock_method.assert_called_with()
...
>>> MyTest('test_something').test_something()

Puede apilar varios decoradores de parches utilizando este patrón:

   >>> class MyTest(unittest.TestCase):
   ...     @patch('package.module.ClassName1')
   ...     @patch('package.module.ClassName2')
   ...     def test_something(self, MockClass2, MockClass1):
   ...         self.assertIs(package.module.ClassName1, MockClass1)
   ...         self.assertIs(package.module.ClassName2, MockClass2)
   ...
   >>> MyTest('test_something').test_something()

Al anidar decoradores de parches, los mocks se pasan a la función
decorada en el mismo orden en que se aplicaron (el orden normal
*Python* que se aplican los decoradores). Esto significa de abajo
hacia arriba, así que en el ejemplo anterior el simulacro de
"test_module.ClassName2" se pasa primero.

También está "patch.dict()" para establecer valores en un diccionario
solo durante un ámbito y restaurar el diccionario a su estado original
cuando finaliza la prueba:

>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

"patch", "patch.object" and "patch.dict" se pueden utilizar como
gestores de contexto.

Donde utilice "patch()" para crear un simulacro para usted, puede
obtener una referencia al simulacro utilizando la forma "as" de la
instrucción with:

>>> class ProductionClass:
...     def method(self):
...         pass
...
>>> with patch.object(ProductionClass, 'method') as mock_method:
...     mock_method.return_value = None
...     real = ProductionClass()
...     real.method(1, 2, 3)
...
>>> mock_method.assert_called_with(1, 2, 3)

Como alternativa "patch", "patch.object" and "patch.dict" se pueden
utilizar como decoradores de clase. Cuando se utiliza de esta manera
es lo mismo que aplicar el decorador individualmente a cada método
cuyo nombre comienza con "test".


Otros Ejemplos
==============

Estos son algunos ejemplos más para algunos escenarios ligeramente más
avanzados.


Mocking de llamadas encadenadas
-------------------------------

Mocking de las llamadas encadenadas es en realidad sencillo con mock
una vez que entiende el atributo "return_value" . Cuando se llama a un
mock por primera vez, o se obtiene su "return_value" antes de que se
llame, se crea un nuevo "Mock".

Esto significa que puede ver cómo se ha utilizado el objeto devuelto
de una llamada a un objeto simulado interrogando el simulado
"return_value":

>>> mock = Mock()
>>> mock().foo(a=2, b=3)
<Mock name='mock().foo()' id='...'>
>>> mock.return_value.foo.assert_called_with(a=2, b=3)

Desde aquí es un paso simple para configurar y luego hacer aserciones
sobre llamadas encadenadas. Por supuesto, otra alternativa es escribir
su código de una manera más comprobable en primer lugar...

Por lo tanto, supongamos que tenemos algún código que se ve un poco
como este:

>>> class Something:
...     def __init__(self):
...         self.backend = BackendProvider()
...     def method(self):
...         response = self.backend.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
...         # more code

Suponiendo que "BackendProvider" ya está bien probado, ¿cómo probamos
"method()"? En concreto, queremos probar que la sección de código "#
more code" usa el objeto de respuesta de la manera correcta.

A medida que esta cadena de llamadas se realiza a partir de un
atributo de instancia, podemos parchear el atributo "backend" en una
instancia de "Something". En este caso en particular sólo estamos
interesados en el valor devuelto de la llamada final a "start_call"
por lo que no tenemos mucha configuración que hacer. Supongamos que el
objeto que devuelve es 'similar a un archivo', por lo que nos
aseguraremos de que nuestro objeto de respuesta utilice el compilado
"open()" as its "spec".

Para ello creamos una instancia mock como nuestro back-end simulado y
creamos un objeto de respuesta mock para ella. Para establecer la
respuesta como el valor devuelto de ese "start_call" final podríamos
hacer lo siguiente:

   mock_backend.get_endpoint.return_value.create_call.return_value.start_call.return_value = mock_response

Podemos hacerlo de una manera un poco mejor usando el método
"configure_mock()" para establecer directamente el valor devuelto para
nosotros:

   >>> something = Something()
   >>> mock_response = Mock(spec=open)
   >>> mock_backend = Mock()
   >>> config = {'get_endpoint.return_value.create_call.return_value.start_call.return_value': mock_response}
   >>> mock_backend.configure_mock(**config)

Con estos monkey patch el "backend simulado" en su lugar y puede hacer
la llamada real:

   >>> something.backend = mock_backend
   >>> something.method()

Usando "mock_calls" podemos comprobar la llamada encadenada con una
sola afirmación. Una llamada encadenada es varias llamadas en una
línea de código, por lo que habrá varias entradas en "mock_calls".
Podemos usar "call.call_list()" para crear esta lista de llamadas para
nosotros:

   >>> chained = call.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
   >>> call_list = chained.call_list()
   >>> assert mock_backend.mock_calls == call_list


Mocking parcial
---------------

En algunas pruebas quería un mock de una llamada a
"datetime.date.today()" para devolver una fecha conocida, pero no
quería evitar que el código sometido a prueba creara nuevos objetos de
fecha. Desafortunadamente "datetime.date" está escrito en C, por lo
que no podía simplemente parchear mono el método estático
"date.today()" .

Encontré una forma sencilla de hacer esto que implicaba ajustar
eficazmente la clase date con un mock, pero pasar llamadas al
constructor a la clase real (y devolver instancias reales).

El "patch decorator" se utiliza aquí como un mock de la clase "date"
en el módulo en pruebas. A continuación, el atributo "side_effect" de
la clase de fecha simulada se establece en una función lambda que
devuelve una fecha real. Cuando la clase de fecha simulada se denomina
fecha real, se construirá y devolverá "side_effect".

   >>> from datetime import date
   >>> with patch('mymodule.date') as mock_date:
   ...     mock_date.today.return_value = date(2010, 10, 8)
   ...     mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
   ...
   ...     assert mymodule.date.today() == date(2010, 10, 8)
   ...     assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)

Tenga en cuenta que no parcheamos "datetime.date" globalmente,
parcheamos "date" en el módulo que *uses*. Consulte where to patch.

Cuando "date.today()" es llamada se retorna una fecha conocida, pero
llama al constructor "date(...)" este constructor todavía devuelve
fechas normales. Sin esto, puede encontrarse teniendo que calcular un
resultado esperado utilizando exactamente el mismo algoritmo que el
código en prueba, que es un anti-patrón de prueba clásico.

Las llamadas al constructor de fecha se registran en los atributos
"mock_date" ("call_count" y amigos) que también pueden ser útiles para
las pruebas.

Una forma alternativa de tratar con fechas de mock, u otras clases
integradas, se discute en esta entrada de blog.


Mocking de un Método Generador
------------------------------

Un generador de Python es una función o método que utiliza la
instrucción "yield" para devolver una serie de valores cuando se itera
sobre [1].

Se llama a un método / función del generador para devolver el objeto
generador. Es el objeto generador que luego se itera. El método de
protocolo para la iteración es "__iter__()", por lo que podemos hacer
mock de esto usando una "MagicMock".

Aquí hay una clase de ejemplo con un método "iter" implementado como
generador:

>>> class Foo:
...     def iter(self):
...         for i in [1, 2, 3]:
...             yield i
...
>>> foo = Foo()
>>> list(foo.iter())
[1, 2, 3]

¿Cómo haríamos un mock de esta clase y, en particular, de su método
"iter"?

Para configurar los valores devueltos desde la iteración (implícita en
la llamada a "list"), necesitamos configurar el objeto devuelto por la
llamada a "foo.iter()".

>>> mock_foo = MagicMock()
>>> mock_foo.iter.return_value = iter([1, 2, 3])
>>> list(mock_foo.iter())
[1, 2, 3]

[1] También hay expresiones generadoras y más usos avanzados
    <http://www.dabeaz.com/coroutines/index.html>'_ de generadores,
    pero no nos preocupan los que están aquí. Una muy buena
    introducción a los generadores y lo potentes que son es: Generator
    Tricks for Systems Programmers.


Aplicar el mismo parche a cada método de prueba
-----------------------------------------------

Si desea que se coloquen varias revisiones para varios métodos de
prueba, la forma obvia es aplicar los decoradores de parches a cada
método. Esto puede parecer una repetición innecesaria. Para Python 2.6
o más reciente puede utilizar "patch()" (en todas sus diversas formas)
como decorador de clases. Esto aplica las revisiones a todos los
métodos de prueba de la clase. Un método de prueba se identifica
mediante métodos cuyos nombres comienzan con "test":

   >>> @patch('mymodule.SomeClass')
   ... class MyTest(unittest.TestCase):
   ...
   ...     def test_one(self, MockSomeClass):
   ...         self.assertIs(mymodule.SomeClass, MockSomeClass)
   ...
   ...     def test_two(self, MockSomeClass):
   ...         self.assertIs(mymodule.SomeClass, MockSomeClass)
   ...
   ...     def not_a_test(self):
   ...         return 'something'
   ...
   >>> MyTest('test_one').test_one()
   >>> MyTest('test_two').test_two()
   >>> MyTest('test_two').not_a_test()
   'something'

Una forma alternativa de administrar parches es usar Métodos start y
stop de patch. Estos le permiten mover el parche en sus métodos
"setUp" y "tearDown".

   >>> class MyTest(unittest.TestCase):
   ...     def setUp(self):
   ...         self.patcher = patch('mymodule.foo')
   ...         self.mock_foo = self.patcher.start()
   ...
   ...     def test_foo(self):
   ...         self.assertIs(mymodule.foo, self.mock_foo)
   ...
   ...     def tearDown(self):
   ...         self.patcher.stop()
   ...
   >>> MyTest('test_foo').run()

Si utiliza esta técnica, debe asegurarse de que la aplicación de
parches se "undone" llamando a "stop". Esto puede ser más complicado
de lo que podría pensar, porque si se produce una excepción en el
setUp, no se llama a tearDown. "unittest.TestCase.addCleanup()" hace
que esto sea más fácil:

   >>> class MyTest(unittest.TestCase):
   ...     def setUp(self):
   ...         patcher = patch('mymodule.foo')
   ...         self.addCleanup(patcher.stop)
   ...         self.mock_foo = patcher.start()
   ...
   ...     def test_foo(self):
   ...         self.assertIs(mymodule.foo, self.mock_foo)
   ...
   >>> MyTest('test_foo').run()


Mocking de métodos sin enlazar
------------------------------

Mientras escribía pruebas hoy en día, necesitaba parchear un método
*unbound method* (parchear el método en la clase en lugar de en la
instancia). Necesitaba que se pasara a sí mismo como primer argumento
porque quiero hacer aserciones sobre qué objetos estaban llamando a
este método en particular. El problema es que no se puede parchear con
un mock para esto, porque si reemplaza un método independiente con un
mock, no se convierte en un método enlazado cuando se obtiene de la
instancia, por lo que no se pasa por sí mismo. La solución es revisar
el método independiente con una función real en su lugar. El decorador
"patch()" hace que sea tan simple parchear métodos con un mock que
tener que crear una función real se convierte en una molestia.

Si pasa "autospec=True" al parche, entonces hace el parche con un
objeto de función *real*. Este objeto de función tiene la misma firma
que el que está reemplazando, pero delega en un mock bajo el capó.
Usted todavía consigue su mock de auto-creado exactamente de la misma
manera que antes. Lo que significa, sin embargo, es que si lo usa para
parchear un método independiente en una clase, la función simulada se
convertirá en un método enlazado si se obtiene de una instancia.
Tendrá "self" pasado como el primer argumento, que es exactamente lo
que quería:

>>> class Foo:
...   def foo(self):
...     pass
...
>>> with patch.object(Foo, 'foo', autospec=True) as mock_foo:
...   mock_foo.return_value = 'foo'
...   foo = Foo()
...   foo.foo()
...
'foo'
>>> mock_foo.assert_called_once_with(foo)

Si no usamos "autospec=True" entonces el método independiente se
parchea con una instancia de Mock en su lugar, y no se llama con
"self".


Comprobación de varias llamadas con mock
----------------------------------------

mock tiene una buena API para hacer aserciones sobre cómo se usan sus
objetos ficticios.

>>> mock = Mock()
>>> mock.foo_bar.return_value = None
>>> mock.foo_bar('baz', spam='eggs')
>>> mock.foo_bar.assert_called_with('baz', spam='eggs')

Si su mock solo se llama una vez, puede usar el método
"assert_called_once_with()" que también afirma que "call_count" es
uno.

>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
>>> mock.foo_bar()
>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
Traceback (most recent call last):
    ...
AssertionError: Expected to be called once. Called 2 times.

Tanto "assert_called_with" como "assert_called_once_with" hacen
afirmaciones sobre la llamada *más reciente*. Si su mock va a ser
llamado varias veces, y desea hacer aserciones sobre *todas* esas
llamadas que puede utilizar "call_args_list":

>>> mock = Mock(return_value=None)
>>> mock(1, 2, 3)
>>> mock(4, 5, 6)
>>> mock()
>>> mock.call_args_list
[call(1, 2, 3), call(4, 5, 6), call()]

El helper "call" facilita la toma de aserciones sobre estas llamadas.
Puede crear una lista de llamadas esperadas y compararla con
"call_args_list". Esto se ve notablemente similar al repr de la
"call_args_list":

>>> expected = [call(1, 2, 3), call(4, 5, 6), call()]
>>> mock.call_args_list == expected
True


Copiando con argumentos mutables
--------------------------------

Otra situación es rara, pero puede morderte, es cuando se llama a tu
mock con argumentos mutables. "call_args" y "call_args_list" almacenan
*referencias* a los argumentos. Si el código sometido a prueba muta
los argumentos ya no puede realizar aserciones sobre cuáles eran los
valores cuando se llamó al mock.

Este es un código de ejemplo que muestra el problema. Imagine las
siguientes funciones definidas en 'mymodule':

   def frob(val):
       pass

   def grob(val):
       "First frob and then clear val"
       frob(val)
       val.clear()

Cuando tratamos de probar que "grob" llama "frob" con el argumento
correcto mira lo que sucede:

   >>> with patch('mymodule.frob') as mock_frob:
   ...     val = {6}
   ...     mymodule.grob(val)
   ...
   >>> val
   set()
   >>> mock_frob.assert_called_with({6})
   Traceback (most recent call last):
       ...
   AssertionError: Expected: (({6},), {})
   Called with: ((set(),), {})

Una posibilidad sería que el mock copiara los argumentos que pasa.
Esto podría causar problemas si realiza aserciones que se basan en la
identidad del objeto para la igualdad.

Aquí hay una solución que utiliza la funcionalidad "side_effect". Si
proporciona una función "side_effect" para un mock, se llamará a
"side_effect" con los mismos argumentos que el mock. Esto nos da la
oportunidad de copiar los argumentos y almacenarlos para aserciones
posteriores. En este ejemplo estoy usando *otro* mock para almacenar
los argumentos de modo que pueda usar los métodos ficticios para hacer
la aserción. Una vez más, una función auxiliar configura esto para mí.

   >>> from copy import deepcopy
   >>> from unittest.mock import Mock, patch, DEFAULT
   >>> def copy_call_args(mock):
   ...     new_mock = Mock()
   ...     def side_effect(*args, **kwargs):
   ...         args = deepcopy(args)
   ...         kwargs = deepcopy(kwargs)
   ...         new_mock(*args, **kwargs)
   ...         return DEFAULT
   ...     mock.side_effect = side_effect
   ...     return new_mock
   ...
   >>> with patch('mymodule.frob') as mock_frob:
   ...     new_mock = copy_call_args(mock_frob)
   ...     val = {6}
   ...     mymodule.grob(val)
   ...
   >>> new_mock.assert_called_with({6})
   >>> new_mock.call_args
   call({6})

"copy_call_args" se llama con el mock que se llamará. Devuelve un
nuevo mock en el que hacemos la aserción. La función "side_effect"
hace una copia de los args y llama a nuestro "new_mock" con la copia.

Nota:

  Si su simulacro solo se va a usar una vez, hay una forma más fácil
  de verificar los argumentos en el punto en que se llaman.
  Simplemente puede hacer la comprobación dentro de una función
  "side_effect".

  >>> def side_effect(arg):
  ...     assert arg == {6}
  ...
  >>> mock = Mock(side_effect=side_effect)
  >>> mock({6})
  >>> mock(set())
  Traceback (most recent call last):
      ...
  AssertionError

Un enfoque alternativo es crear una subclase de "Mock" o "MagicMock"
que copie (usando "copy.deepcopy()") los argumentos. A continuación se
muestra un ejemplo de implementación:

>>> from copy import deepcopy
>>> class CopyingMock(MagicMock):
...     def __call__(self, /, *args, **kwargs):
...         args = deepcopy(args)
...         kwargs = deepcopy(kwargs)
...         return super().__call__(*args, **kwargs)
...
>>> c = CopyingMock(return_value=None)
>>> arg = set()
>>> c(arg)
>>> arg.add(1)
>>> c.assert_called_with(set())
>>> c.assert_called_with(arg)
Traceback (most recent call last):
    ...
AssertionError: Expected call: mock({1})
Actual call: mock(set())
>>> c.foo
<CopyingMock name='mock.foo' id='...'>

Cuando subclases "Mock" o "MagicMock" todos los atributos creados
dinámicamente, y el "return_value" usará tu subclase automáticamente.
Eso significa que todos los elementos secundarios de un "CopyingMock"
también tendrán el tipo "CopyingMock".


Anidando Parches
----------------

Usar parches como administradores de contexto es bueno, pero si haces
varios parches, puedes terminar con instrucciones anidadas que se
sangran cada vez más a la derecha:

   >>> class MyTest(unittest.TestCase):
   ...
   ...     def test_foo(self):
   ...         with patch('mymodule.Foo') as mock_foo:
   ...             with patch('mymodule.Bar') as mock_bar:
   ...                 with patch('mymodule.Spam') as mock_spam:
   ...                     assert mymodule.Foo is mock_foo
   ...                     assert mymodule.Bar is mock_bar
   ...                     assert mymodule.Spam is mock_spam
   ...
   >>> original = mymodule.Foo
   >>> MyTest('test_foo').test_foo()
   >>> assert mymodule.Foo is original

Con las funciones unittest "cleanup" y Métodos start y stop de patch
podemos lograr el mismo efecto sin la sangría anidada. Un método
auxiliar simple, "create_patch", pone el parche en su lugar y devuelve
el mock creado para nosotros:

   >>> class MyTest(unittest.TestCase):
   ...
   ...     def create_patch(self, name):
   ...         patcher = patch(name)
   ...         thing = patcher.start()
   ...         self.addCleanup(patcher.stop)
   ...         return thing
   ...
   ...     def test_foo(self):
   ...         mock_foo = self.create_patch('mymodule.Foo')
   ...         mock_bar = self.create_patch('mymodule.Bar')
   ...         mock_spam = self.create_patch('mymodule.Spam')
   ...
   ...         assert mymodule.Foo is mock_foo
   ...         assert mymodule.Bar is mock_bar
   ...         assert mymodule.Spam is mock_spam
   ...
   >>> original = mymodule.Foo
   >>> MyTest('test_foo').run()
   >>> assert mymodule.Foo is original


Mocking a un diccionario usando MagickMock
------------------------------------------

Es posible que desee simular un diccionario u otro objeto contenedor,
registrando todo el acceso a él mientras todavía se comporta como un
diccionario.

Podemos hacer esto con "MagicMock", que se comportará como un
diccionario, y usando "side_effect" para delegar el acceso del
diccionario a un diccionario subyacente real que está bajo nuestro
control.

Cuando se llama a los métodos "__getitem__()" y "__setitem__()" de
nuestro "MagicMock" (acceso normal al diccionario), entonces se llama
a "side_effect" con la clave (y en el caso de "__setitem__" el valor
también). También podemos controlar lo que se devuelve.

Después de que se haya utilizado el "MagicMock" podemos usar atributos
como "call_args_list" para afirmar cómo se usó el diccionario:

>>> my_dict = {'a': 1, 'b': 2, 'c': 3}
>>> def getitem(name):
...      return my_dict[name]
...
>>> def setitem(name, val):
...     my_dict[name] = val
...
>>> mock = MagicMock()
>>> mock.__getitem__.side_effect = getitem
>>> mock.__setitem__.side_effect = setitem

Nota:

  Una alternativa al uso de "MagicMock" es usar "Mock" y *solo*
  proporcionar los métodos mágicos que desea específicamente:

  >>> mock = Mock()
  >>> mock.__getitem__ = Mock(side_effect=getitem)
  >>> mock.__setitem__ = Mock(side_effect=setitem)

  Una *tercera* opción es usar "MagicMock" pero pasando "dict" como el
  argumento *spec* (o *spec_set*) para que el "MagicMock" creado solo
  tenga métodos mágicos de diccionario disponibles:

  >>> mock = MagicMock(spec_set=dict)
  >>> mock.__getitem__.side_effect = getitem
  >>> mock.__setitem__.side_effect = setitem

Con estas funciones de efectos secundarios en su lugar, el "mock" se
comportará como un diccionario normal pero registrando el acceso.
Incluso genera un "KeyError" si intenta acceder a una clave que no
existe.

>>> mock['a']
1
>>> mock['c']
3
>>> mock['d']
Traceback (most recent call last):
    ...
KeyError: 'd'
>>> mock['b'] = 'fish'
>>> mock['d'] = 'eggs'
>>> mock['b']
'fish'
>>> mock['d']
'eggs'

Una vez utilizado, puede realizar aserciones sobre el acceso
utilizando los métodos y atributos mock normales:

>>> mock.__getitem__.call_args_list
[call('a'), call('c'), call('d'), call('b'), call('d')]
>>> mock.__setitem__.call_args_list
[call('b', 'fish'), call('d', 'eggs')]
>>> my_dict
{'a': 1, 'b': 'fish', 'c': 3, 'd': 'eggs'}


Mock de subclases y sus atributos
---------------------------------

Hay varias razones por las que es posible que desee crear subclases
"Mock" Una razón podría ser agregar métodos auxiliares. Aquí hay un
ejemplo simple:

>>> class MyMock(MagicMock):
...     def has_been_called(self):
...         return self.called
...
>>> mymock = MyMock(return_value=None)
>>> mymock
<MyMock id='...'>
>>> mymock.has_been_called()
False
>>> mymock()
>>> mymock.has_been_called()
True

El comportamiento estándar para las instancias "Mock" es que los
atributos y los simulacros de valor devuelto son del mismo tipo que el
simulacro en el que se accede a ellos. Esto garantiza que los
atributos "Mock" son "Mocks" y los atributos "MagicMock" son
"MagicMocks" [2].  Por lo tanto, si está creando subclases para
agregar métodos auxiliares, también estarán disponibles en los
atributos y el valor devuelto mock de las instancias de su subclase.

>>> mymock.foo
<MyMock name='mock.foo' id='...'>
>>> mymock.foo.has_been_called()
False
>>> mymock.foo()
<MyMock name='mock.foo()' id='...'>
>>> mymock.foo.has_been_called()
True

A veces esto es inconveniente. Por ejemplo, un usuario está creando
subclases de mock para crear un Twisted adaptor. Tener esto aplicado a
los atributos también puede causar errores en realidad.

"Mock" (en todos sus sabores) utiliza un método llamado
"_get_child_mock" para crear estos "sub-mocks" para atributos y
valores devueltos. Puede evitar que la subclase se utilice para los
atributos invalidando este método. La firma es que toma argumentos de
palabra clave arbitrarios ("**kwargs") que luego se pasan al
constructor ficticio:

>>> class Subclass(MagicMock):
...     def _get_child_mock(self, /, **kwargs):
...         return MagicMock(**kwargs)
...
>>> mymock = Subclass()
>>> mymock.foo
<MagicMock name='mock.foo' id='...'>
>>> assert isinstance(mymock, Subclass)
>>> assert not isinstance(mymock.foo, Subclass)
>>> assert not isinstance(mymock(), Subclass)

[2] Una excepción a esta regla son los mock no invocables. Los
    atributos usan la variante a la que se puede llamar porque, de lo
    contrario, los simulacros a los que no se puede llamar no podrían
    tener métodos a los que se puede llamar.


Importaciones de Mocking con patch.dict
---------------------------------------

Una situación en la que un mocking puede ser difícil es cuando tiene
una importación local dentro de una función. Estos son más difíciles
de simular porque no están usando un objeto del espacio de nombres del
módulo que podemos revisar.

Por lo general, deben evitarse las importaciones locales. A veces se
hacen para evitar dependencias circulares, para las que hay
*generalmente* una manera mucho mejor de resolver el problema
(refactorizar el código) o para evitar "costos iniciales" retrasando
la importación. Esto también se puede resolver de mejores maneras que
una importación local incondicional (almacenar el módulo como una
clase o atributo de módulo y solo hacer la importación en el primer
uso).

Aparte de eso, hay una manera de usar "mock" para afectar los
resultados de una importación. La importación obtiene un *objeto* del
diccionario "sys.modules". Tenga en cuenta que obtiene un *objeto*,
que no tiene por qué ser un módulo. La importación de un módulo por
primera vez da como resultado que un objeto de módulo se coloque en
*sys.modules*, por lo que normalmente cuando importa algo, recupera un
módulo. Sin embargo, este no tiene por qué ser el caso.

Esto significa que puede usar "patch.dict()" para *temporalmente*
poner un mock en su lugar sobre "sys.modules". Cualquier importación
mientras este parche está activo recuperará el mock. Cuando el parche
está completo (la función decorada sale, el cuerpo de la instrucción
with está completo o se llama a "patcher.stop()") entonces lo que
había anteriormente se restaurará de forma segura.

Aquí hay un ejemplo de mock del módulo 'fooble'.

>>> import sys
>>> mock = Mock()
>>> with patch.dict('sys.modules', {'fooble': mock}):
...    import fooble
...    fooble.blob()
...
<Mock name='mock.blob()' id='...'>
>>> assert 'fooble' not in sys.modules
>>> mock.blob.assert_called_once_with()

Como puede ver, el "import fooble" tiene éxito, pero al salir no queda
ningún 'fooble' en "sys.modules".

Esto también funciona para el formulario "from module import name":

>>> mock = Mock()
>>> with patch.dict('sys.modules', {'fooble': mock}):
...    from fooble import blob
...    blob.blip()
...
<Mock name='mock.blob.blip()' id='...'>
>>> mock.blob.blip.assert_called_once_with()

Con un poco más de trabajo también puede simular las importaciones de
paquetes:

>>> mock = Mock()
>>> modules = {'package': mock, 'package.module': mock.module}
>>> with patch.dict('sys.modules', modules):
...    from package.module import fooble
...    fooble()
...
<Mock name='mock.module.fooble()' id='...'>
>>> mock.module.fooble.assert_called_once_with()


Seguimiento del orden de las llamadas y de las aserciones de llamadas menos detalladas
--------------------------------------------------------------------------------------

La clase "Mock" le permite realizar un seguimiento del *orden* de las
llamadas a métodos en sus objetos mock a través del atributo
"method_calls". Esto no le permite rastrear el orden de las llamadas
entre objetos simulados separados, sin embargo, podemos usar
"mock_calls" para lograr el mismo efecto.

Debido a que los mocks rastrean las llamadas a mocks secundarios en
"mock_calls", y el acceso a un atributo arbitrario de un mock crea un
mock secundario, podemos crear nuestros mocks separados de uno
primario. Las llamadas a esos mocks hijos se grabarán, en orden, en el
"mock_calls" del padre:

>>> manager = Mock()
>>> mock_foo = manager.foo
>>> mock_bar = manager.bar

>>> mock_foo.something()
<Mock name='mock.foo.something()' id='...'>
>>> mock_bar.other.thing()
<Mock name='mock.bar.other.thing()' id='...'>

>>> manager.mock_calls
[call.foo.something(), call.bar.other.thing()]

A continuación, podemos realizar afirmaciones sobre las llamadas,
incluido el orden, comparando con el atributo "mock_calls" en el mock
administrador:

>>> expected_calls = [call.foo.something(), call.bar.other.thing()]
>>> manager.mock_calls == expected_calls
True

Si "patch" está creando y poniendo en su lugar, sus mocks, puede
adjuntarlos a un mock administrador usando el método "attach_mock()" .
Después de adjuntar las llamadas se registrarán en "mock_calls" del
administrador.

   >>> manager = MagicMock()
   >>> with patch('mymodule.Class1') as MockClass1:
   ...     with patch('mymodule.Class2') as MockClass2:
   ...         manager.attach_mock(MockClass1, 'MockClass1')
   ...         manager.attach_mock(MockClass2, 'MockClass2')
   ...         MockClass1().foo()
   ...         MockClass2().bar()
   <MagicMock name='mock.MockClass1().foo()' id='...'>
   <MagicMock name='mock.MockClass2().bar()' id='...'>
   >>> manager.mock_calls
   [call.MockClass1(),
   call.MockClass1().foo(),
   call.MockClass2(),
   call.MockClass2().bar()]

Si se han realizado muchas llamadas, pero solo está interesado en una
secuencia particular de ellas, entonces una alternativa es usar el
método "assert_has_calls()" . Esto toma una lista de llamadas
(construidas con el objeto "call"). Si esa secuencia de llamadas sobre
"mock_calls", la aserción se realiza correctamente.

>>> m = MagicMock()
>>> m().foo().bar().baz()
<MagicMock name='mock().foo().bar().baz()' id='...'>
>>> m.one().two().three()
<MagicMock name='mock.one().two().three()' id='...'>
>>> calls = call.one().two().three().call_list()
>>> m.assert_has_calls(calls)

Aunque la llamada encadenada "m.one().two().three()" no son las únicas
llamadas que se han realizado al mock, la aserción aún tiene éxito.

A veces, un mock puede tener varias llamadas, y solo le interesa
afirmar sobre *algunas* de esas llamadas. Puede que ni siquiera
importe el pedido. En este caso, puede pasar "any_order=True" a
"assert_has_calls":

>>> m = MagicMock()
>>> m(1), m.two(2, 3), m.seven(7), m.fifty('50')
(...)
>>> calls = [call.fifty('50'), call(1), call.seven(7)]
>>> m.assert_has_calls(calls, any_order=True)


Coincidencia de argumentos más compleja
---------------------------------------

Usando el mismo concepto básico que "ANY" podemos implementar
comparadores para hacer afirmaciones más complejas en objetos usados
como argumentos para mocks.

Supongamos que esperamos que algún objeto se pase a un mock que, por
defecto, se compara igual en función de la identidad del objeto (que
es el valor predeterminado de Python para las clases definidas por el
usuario). Para usar "assert_called_with()" tendríamos que pasar
exactamente el mismo objeto. Si solo estamos interesados en algunos de
los atributos de este objeto, podemos crear un comparador que
verifique estos atributos por nosotros.

Puede ver en este ejemplo cómo una llamada 'estándar' a
"assert_called_with" no es suficiente:

>>> class Foo:
...     def __init__(self, a, b):
...         self.a, self.b = a, b
...
>>> mock = Mock(return_value=None)
>>> mock(Foo(1, 2))
>>> mock.assert_called_with(Foo(1, 2))
Traceback (most recent call last):
    ...
AssertionError: Expected: call(<__main__.Foo object at 0x...>)
Actual call: call(<__main__.Foo object at 0x...>)

Una función de comparación para nuestra clase "Foo" podría verse así:

>>> def compare(self, other):
...     if not type(self) == type(other):
...         return False
...     if self.a != other.a:
...         return False
...     if self.b != other.b:
...         return False
...     return True
...

Y un objeto comparador que puede usar funciones de comparación como
esta para su operación de igualdad se vería así:

>>> class Matcher:
...     def __init__(self, compare, some_obj):
...         self.compare = compare
...         self.some_obj = some_obj
...     def __eq__(self, other):
...         return self.compare(self.some_obj, other)
...

Poniendo todo esto junto:

>>> match_foo = Matcher(compare, Foo(1, 2))
>>> mock.assert_called_with(match_foo)

El "Matcher" es instanciado con nuestra función de comparación y el
objeto "Foo" con el que queremos comparar. En "assert_called_with" se
llamará al método de igualdad "Matcher", que compara el objeto con el
que se llamó al mock con el que creamos nuestro matcher. Si coinciden,
entonces pasa "assert_called_with", y si no lo hacen, se generará un
"AssertionError":

>>> match_wrong = Matcher(compare, Foo(3, 4))
>>> mock.assert_called_with(match_wrong)
Traceback (most recent call last):
    ...
AssertionError: Expected: ((<Matcher object at 0x...>,), {})
Called with: ((<Foo object at 0x...>,), {})

Con un poco de ajuste, podría hacer que la función de comparación
genere el "AssertionError" directamente y proporcione un mensaje de
falla más útil.

A partir de la versión 1.5, la biblioteca de pruebas de Python
PyHamcrest proporciona una funcionalidad similar, que puede ser útil
aquí, en la forma de su comparador de igualdad
(hamcrest.library.integration.match_equality).
