Burlarse de una función para generar una excepción para probar un bloque excepto

118

Tengo una función ( foo) que llama a otra función ( bar). Si la invocación bar()genera un HttpError, quiero manejarlo especialmente si el código de estado es 404; de lo contrario, vuelva a aumentar.

Estoy tratando de escribir algunas pruebas unitarias en torno a esta foofunción, burlándome de la llamada a bar(). Desafortunadamente, no puedo obtener la llamada simulada bar()para generar una excepción que es detectada por mi exceptbloqueo.

Aquí está mi código que ilustra mi problema:

import unittest
import mock
from apiclient.errors import HttpError


class FooTests(unittest.TestCase):
    @mock.patch('my_tests.bar')
    def test_foo_shouldReturnResultOfBar_whenBarSucceeds(self, barMock):
        barMock.return_value = True
        result = foo()
        self.assertTrue(result)  # passes

    @mock.patch('my_tests.bar')
    def test_foo_shouldReturnNone_whenBarRaiseHttpError404(self, barMock):
        barMock.side_effect = HttpError(mock.Mock(return_value={'status': 404}), 'not found')
        result = foo()
        self.assertIsNone(result)  # fails, test raises HttpError

    @mock.patch('my_tests.bar')
    def test_foo_shouldRaiseHttpError_whenBarRaiseHttpErrorNot404(self, barMock):
        barMock.side_effect = HttpError(mock.Mock(return_value={'status': 500}), 'error')
        with self.assertRaises(HttpError):  # passes
            foo()

def foo():
    try:
        result = bar()
        return result
    except HttpError as error:
        if error.resp.status == 404:
            print '404 - %s' % error.message
            return None
        raise

def bar():
    raise NotImplementedError()

Seguí los documentos de Mock que dicen que debe establecer el side_effectde una Mockinstancia en una Exceptionclase para que la función simulada genere el error.

También miré algunas otras preguntas y respuestas de StackOverflow relacionadas, y parece que estoy haciendo lo mismo que ellos están haciendo para causar una Excepción que se plantee por su simulacro.

¿Por qué establecer el side_effectde barMockno causa que se aumente lo esperado Exception? Si estoy haciendo algo extraño, ¿cómo debo probar la lógica en mi exceptbloque?

Jesse Webb
fuente
Estoy bastante seguro de que su excepción está siendo criado, pero no estoy seguro de cómo va a configurar el resp.statuscódigo de allí. De donde HTTPErrorviene
Martijn Pieters
@MartijnPieters HttpErrores una clase definida en el de Google apiclientlib que utilizamos en GAE. Está __init__definido con los parámetros, (resp, content)por lo que estaba intentando crear una instancia simulada para la respuesta, con el código de estado apropiado especificado.
Jesse Webb
Bien, así es esta clase ; pero no es necesario utilizarlo return_valueentonces; respno está siendo llamado .
Martijn Pieters
1
Probé mi código nuevamente sin usar HttpErrory en su lugar intenté usar solo una Exceptioninstancia normal . Esto funciona perfectamente. Esto significa que debe tener algo que ver con cómo estoy configurando la HttpErrorinstancia, probablemente relacionado con cómo estoy creando una Mockinstancia para la respuesta.
Jesse Webb

Respuestas:

141

Su simulacro está generando la excepción muy bien, pero error.resp.statusfalta el valor. En lugar de usar return_value, solo diga Mockque statuses un atributo:

barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')

Los argumentos de palabras clave adicionales Mock()se establecen como atributos en el objeto resultante.

Puse sus definiciones fooy baren un my_testsmódulo, agregado en la HttpErrorclase para que yo también pudiera usarlo, y su prueba se puede ejecutar con éxito:

>>> from my_tests import foo, HttpError
>>> import mock
>>> with mock.patch('my_tests.bar') as barMock:
...     barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')
...     result = my_test.foo()
... 
404 - 
>>> result is None
True

Incluso puedes ver la print '404 - %s' % error.messagelínea correr, pero creo que querías usarla error.contentallí; ese es el HttpError()conjunto de atributos del segundo argumento, en cualquier caso.

Martijn Pieters
fuente
2
la side_effectes la parte clave
Daniel Butler