Nick Humrich
Estoy usando pythons mock.patch y me gustaría cambiar el valor de retorno para cada llamada. Aquí está la advertencia: la función que se parchea no tiene entradas, por lo que no puedo cambiar el valor de retorno en función de la entrada.
Aquí está mi código para referencia.
def get_boolean_response():
response = io.prompt('y/n').lower()
while response not in ('y', 'n', 'yes', 'no'):
io.echo('Not a valid input. Try again'])
response = io.prompt('y/n').lower()
return response in ('y', 'yes')
Mi código de prueba:
@mock.patch('io')
def test_get_boolean_response(self, mock_io):
#setup
mock_io.prompt.return_value = ['x','y']
result = operations.get_boolean_response()
#test
self.assertTrue(result)
self.assertEqual(mock_io.prompt.call_count, 2)
io.prompt
es solo una versión independiente de la plataforma (python 2 y 3) de “input”. Entonces, en última instancia, estoy tratando de simular la entrada de los usuarios. He intentado usar una lista para el valor de retorno, pero eso no parece funcionar.
Puede ver que si el valor de retorno es algo inválido, simplemente obtendré un bucle infinito aquí. Así que necesito una forma de cambiar eventualmente el valor de retorno, para que mi prueba realmente termine.
(otra forma posible de responder a esta pregunta podría ser explicar cómo podría imitar la entrada del usuario en una prueba unitaria)
No es un duplicado de esta pregunta principalmente porque no tengo la capacidad de variar las entradas.
Uno de los comentarios de la Respuesta sobre esta pregunta está en la misma línea, pero no se ha proporcionado ninguna respuesta/comentario.
Martijn Pieters
Puede asignar un iterable a side_effect
y el simulacro devolverá el siguiente valor en la secuencia cada vez que se llame:
>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.side_effect = ['foo', 'bar', 'baz']
>>> m()
'foo'
>>> m()
'bar'
>>> m()
'baz'
citando el Mock()
documentación:
Si efecto secundario es un iterable, cada llamada al simulacro devolverá el siguiente valor del iterable.
-
¿Hay alguna manera de hacer esto con el estándar?
mock
? ¿Hay alguna manera de usar el parche con MagicMock como lo estoy haciendo con el simulacro estándar?– Nick Humrich
22 de julio de 2014 a las 20:39
-
@Humdinger: Esta es una característica del stardard
Mock
clase.– Martijn Pieters
♦22 de julio de 2014 a las 20:39
-
La asignación de una lista parece funcionar solo con Python 3. Probando con python 2.7 Necesito usar un iterador en su lugar (
m.side_effect = iter(['foo', 'bar', 'baz'])
).– usuario686249
12 de agosto de 2015 a las 12:54
-
@user686249: De hecho, puedo reproducir esto, porque la especificación de un método produce un
lambda
(una función), no unaMagicMock
. Un objeto de función no puede tener propiedades, por lo queside_effect
atributo posee ser un iterable. Sin embargo, no debería especificar el método de esa manera. mejor usomock.patch.object(requests.Session, 'post')
; que da como resultado un objeto parcheador que se auto-especifica correctamente en el método, y apoyaside_effect
adecuadamente.– Martijn Pieters
♦14 de agosto de 2015 a las 10:27
-
@JoeMjr2: cuando se agota el iterador,
StopIteration
es elevado. Puede usar cualquier iterador, por lo que podría usaritertools.chain(['Foo'], itertools.repeat('Bar'))
para producirFoo
una vez, luego producir para siempreBar
.– Martijn Pieters
♦23 de febrero de 2019 a las 15:38
para múltiples valores de retorno, podemos usar efecto secundario durante la inicialización del parche también y pase iterable a él
muestra.py
def hello_world():
pass
prueba_muestra.py
from unittest.mock import patch
from sample import hello_world
@patch('sample.hello_world', side_effect=[{'a': 1, 'b': 2}, {'a': 4, 'b': 5}])
def test_first_test(self, hello_world_patched):
assert hello_world() == {'a': 1, 'b': 2}
assert hello_world() == {'a': 4, 'b': 5}
assert hello_world_patched.call_count == 2
También puede usar patch para múltiples valores de retorno:
@patch('Function_to_be_patched', return_value=['a', 'b', 'c'])
Recuerde que si está utilizando más de un parche para un método, el orden se verá así:
@patch('a')
@patch('b')
def test(mock_b, mock_a);
pass
como se puede ver aquí, se revertirá. El primer parche mencionado estará en la última posición.
-
esto no funciona Simplemente devuelve la lista completa.
['a', 'b', 'c']
cada vez que llame a la función parcheada.– Cerín
15 de mayo de 2021 a las 3:44
response is not 'y' or 'n' or 'yes' or 'no'
en no haciendo lo que crees que hace. Consulte ¿Cómo pruebo una variable con varios valores? y deberías no usaris
para comparar valores de cadena, use==
comparar valoresno identidades de objetos.– Martijn Pieters
♦
22 de julio de 2014 a las 20:32
También tenga cuidado aquí. Parece que estás tratando de usar
is
para comparar literales de cadena. No hagas eso. El hecho de que funcione (a veces) es solo un detalle de implementación en CPython. También,response is not 'y' or 'n' or 'yes' or 'no'
probablemente no está haciendo lo que crees que es…– mgilson
22 de julio de 2014 a las 20:32
Puedes usar
@patch('foo.bar', side_effect=['ret1', ret2', 'ret3'])
.– Asclepio
8 oct 2020 a las 18:22