assert_has_calls
Es otro enfoque para este problema.
De los documentos:
Claim_has_calls (llamadas, any_order = False)
afirmar que se ha llamado al simulacro con las llamadas especificadas. La lista de llamadas simuladas se verifica para las llamadas.
Si any_order es False (el valor predeterminado), las llamadas deben ser secuenciales. Puede haber llamadas adicionales antes o después de las llamadas especificadas.
Si any_order es True, las llamadas pueden estar en cualquier orden, pero todas deben aparecer en simulacros de llamadas.
Ejemplo:
>>> from unittest.mock import call, Mock
>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
Fuente: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_has_calls
tuple
:isinstance(mock.call(1), tuple)
daTrue
. También agregaron algunos métodos y atributos.side_effect
Por lo general, no me importa el orden de las llamadas, solo que ocurrieron. En ese caso, combino
assert_any_call
con una afirmación sobrecall_count
.Creo que hacerlo de esta manera es más fácil de leer y comprender que una gran lista de llamadas transferidas a un solo método.
Si le importa el orden o espera múltiples llamadas idénticas,
assert_has_calls
podría ser más apropiado.Editar
Desde que publiqué esta respuesta, he repensado mi enfoque de las pruebas en general. Creo que vale la pena mencionar que si su prueba se está volviendo tan complicada, es posible que esté haciendo pruebas inapropiadamente o tenga un problema de diseño. Los simulacros están diseñados para probar la comunicación entre objetos en un diseño orientado a objetos. Si su diseño no está orientado a objeciones (como en procedimientos o procedimientos más funcionales), la simulación puede ser totalmente inapropiada. Es posible que también tenga demasiadas cosas dentro del método, o puede estar probando detalles internos que es mejor dejar sin desmontar. Desarrollé la estrategia mencionada en este método cuando mi código no estaba muy orientado a objetos, y creo que también estaba probando detalles internos que habría sido mejor dejar sin descifrar.
fuente
do() if TEST_ENV=='prod' else dont()
), se logra fácilmente burlándose de la forma que sugirió. un efecto secundario de esto es mantener pruebas por versiones (digamos cambios de código entre la API de búsqueda de Google v1 y v2, su código probará la versión 1 sin importar qué)Puede usar el
Mock.call_args_list
atributo para comparar parámetros con llamadas a métodos anteriores. Eso junto con elMock.call_count
atributo debería darte el control total.fuente
assert_has_calls
solo verifica si se han realizado las llamadas esperadas, pero no si esas son las únicas.Siempre tengo que mirar esto una y otra vez, así que aquí está mi respuesta.
Afirmar llamadas a métodos múltiples en diferentes objetos de la misma clase
Supongamos que tenemos una clase de trabajo pesado (que queremos burlarnos):
Aquí hay un código que usa dos instancias de la
HeavyDuty
clase:Ahora, aquí hay un caso de prueba para la
heavy_work
función:Nos estamos burlando de la
HeavyDuty
clase conMockHeavyDuty
. Para hacer valer las llamadas a métodos provenientes de cadaHeavyDuty
instancia, tenemos que referirnosMockHeavyDuty.return_value.assert_has_calls
, en lugar deMockHeavyDuty.assert_has_calls
. Además, en la lista deexpected_calls
tenemos que especificar qué nombre de método estamos interesados en reclamar llamadas. Entonces nuestra lista está hecha de llamadas acall.do_work
, en lugar de simplementecall
.El ejercicio del caso de prueba nos muestra que es exitoso:
Si modificamos la
heavy_work
función, la prueba falla y produce un mensaje de error útil:Afirmar múltiples llamadas a una función
Para contrastar con lo anterior, aquí hay un ejemplo que muestra cómo simular múltiples llamadas a una función:
Hay dos diferencias principales. La primera es que, al burlarnos de una función, configuramos nuestras llamadas esperadas usando
call
, en lugar de usarcall.some_method
. La segunda es que nosotros llamamosassert_has_calls
enmock_work_function
vez de sobremock_work_function.return_value
.fuente