Afirmar que una función / método no fue llamado usando Mock

131

Estoy usando la biblioteca Mock para probar mi aplicación, pero quiero afirmar que no se llamó a alguna función. Los documentos simulados hablan sobre métodos como mock.assert_called_withy mock.assert_called_once_with, pero no encontré nada parecido mock.assert_not_calledo algo relacionado para verificar que NO se llamara simulacro .

Podría ir con algo como lo siguiente, aunque no parece genial ni pitónico:

def test_something:
    # some actions
    with patch('something') as my_var:
        try:
            # args are not important. func should never be called in this test
            my_var.assert_called_with(some, args)
        except AssertionError:
            pass  # this error being raised means it's ok
    # other stuff

¿Alguna idea de cómo lograr esto?

Gerard
fuente
Como @Ahmet señala en su respuesta, afirmar_not_called ahora es compatible, también en el backport ( docs.python.org/3/library/… ).
Martin

Respuestas:

144

Esto debería funcionar para su caso;

assert not my_var.called, 'method should not have been called'

Muestra;

>>> mock=Mock()
>>> mock.a()
<Mock name='mock.a()' id='4349129872'>
>>> assert not mock.b.called, 'b was called and should not have been'
>>> assert not mock.a.called, 'a was called and should not have been'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: a was called and should not have been
Joachim Isaksson
fuente
¿Esta respuesta requiere Django? Recibo un error:AttributeError: MockCallable instance has no attribute 'called'
Nathan Arthur
@NathanArthur Hm, no lo creo, después sudo easy_install -U mocky from mock import Mocken MacOS, lo anterior se ejecuta sin problemas. Nunca instalé Django :)
Joachim Isaksson
Hmm Eso es extraño. Estoy ejecutando Python 2.7.1 y estoy usando unittest y from mock import Mockcon Python Mock 0.1.0 para mis pruebas. ¿Algo de eso suena problemático?
Nathan Arthur
Me estoy burlando de una clase invocable desde otro módulo, por lo que parece module_to_test.another_module.class = mock.Mock(), ¿puede confirmar que no recuerda llamadas en diferentes casos de prueba (unittest.TestCase)? Creo que el recuento de llamadas no se restablece en este caso
0xc0de
66

Aunque es una vieja pregunta, me gustaría agregar que actualmente la mockbiblioteca (backport de unittest.mock) admite el assert_not_calledmétodo.

Solo actualiza el tuyo;

pip install mock --upgrade

Ahmet
fuente
29

Puede verificar el calledatributo, pero si su afirmación falla, lo siguiente que querrá saber es algo sobre la llamada inesperada, por lo que también puede organizar que esa información se muestre desde el principio. Usando unittest, puede verificar el contenido de en su call_args_listlugar:

self.assertItemsEqual(my_var.call_args_list, [])

Cuando falla, da un mensaje como este:

AssertionError: los recuentos de elementos no fueron iguales:
Primero tiene 0, Segundo tiene 1: llamada ('primer argumento', 4)
Rob Kennedy
fuente
14

Cuando prueba usando la clase hereda unittest.TestCase , simplemente puede usar métodos como:

  • afirmar verdadero
  • afirmar falso
  • afirmar Igual

y similar (en la documentación de Python encontrará el resto).

En su ejemplo, simplemente podemos afirmar si la propiedad mock_method.called es False , lo que significa que no se llamó al método.

import unittest
from unittest import mock

import my_module

class A(unittest.TestCase):
    def setUp(self):
        self.message = "Method should not be called. Called {times} times!"

    @mock.patch("my_module.method_to_mock")
    def test(self, mock_method):
        my_module.method_to_mock()

        self.assertFalse(mock_method.called,
                         self.message.format(times=mock_method.call_count))
Hunter_71
fuente
11

Con python >= 3.5usted puede usar mock_object.assert_not_called().

valex
fuente
1

A juzgar por otras respuestas, nadie excepto @ rob-kennedy habló sobre el call_args_list.

Es una herramienta poderosa para que pueda implementar exactamente lo contrario de MagicMock.assert_called_with()

call_args_listes una lista de callobjetos Cada callobjeto representa una llamada realizada en una llamada simulada.

>>> from unittest.mock import MagicMock
>>> m = MagicMock()
>>> m.call_args_list
[]
>>> m(42)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42)]
>>> m(42, 30)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42), call(42, 30)]

Consumir un callobjeto es fácil, ya que puede compararlo con una tupla de longitud 2 donde el primer componente es una tupla que contiene todos los argumentos posicionales de la llamada relacionada, mientras que el segundo componente es un diccionario de los argumentos de palabras clave.

>>> ((42,),) in m.call_args_list
True
>>> m(42, foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((42,), {'foo': 'bar'}) in m.call_args_list
True
>>> m(foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((), {'foo': 'bar'}) in m.call_args_list
True

Entonces, una forma de abordar el problema específico del OP es

def test_something():
    with patch('something') as my_var:
        assert ((some, args),) not in my_var.call_args_list

Tenga en cuenta que de esta manera, en lugar de simplemente verificar si se ha llamado a una llamada simulada, a través de MagicMock.called, ahora puede verificar si se ha llamado con un conjunto específico de argumentos.

Eso es útil Supongamos que desea probar una función que toma una lista y llamar a otra función compute(), para cada uno de los valores de la lista solo si cumplen una condición específica.

Ahora puede burlarse computey probar si ha sido invocado en algún valor pero no en otros.

Giuseppe Crinò
fuente