Zombar de uma função para levantar uma exceção para testar um bloco exceto

118

Eu tenho uma função ( foo) que chama outra função ( bar). Se a invocação bar()gerar um HttpError, quero lidar com isso especialmente se o código de status for 404, caso contrário, aumente novamente.

Estou tentando escrever alguns testes de unidade em torno dessa foofunção, zombando da chamada para bar(). Infelizmente, não consigo fazer a chamada simulada bar()para gerar uma exceção que é capturada pelo meu exceptbloco.

Aqui está meu código que ilustra meu 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()

Eu segui os documentos Mock que dizem que você deve definir o side_effectde uma Mockinstância para uma Exceptionclasse para que a função simulada eleve o erro.

Eu também olhei algumas outras perguntas e respostas relacionadas ao StackOverflow e parece que estou fazendo a mesma coisa que eles estão fazendo para causar e Exception ser gerado por seu mock.

Por que configurar o side_effectde barMocknão causar o Exceptionaumento do esperado ? Se estou fazendo algo estranho, como devo proceder para testar a lógica no meu exceptbloco?

Jesse Webb
fonte
Tenho certeza que sua exceção está sendo gerada, mas não tenho certeza de como você está configurando o resp.statuscódigo lá. De onde HTTPErrorvem?
Martijn Pieters
@MartijnPieters HttpErroré uma classe definida na biblioteca do Googleapiclient que usamos no GAE. Ele __init__é definido com os parâmetros, (resp, content)então eu estava tentando criar uma instância simulada para a resposta, com o código de status apropriado especificado.
Jesse Webb
Certo, então é esta classe ; mas você não precisa usar return_value; respnão está sendo chamado .
Martijn Pieters
1
Tentei meu código novamente sem usar HttpErrore, em vez disso, tentei usar apenas uma Exceptioninstância normal . Isso funciona perfeitamente. Isso significa que deve ter algo a ver com como estou configurando a HttpErrorinstância, provavelmente relacionado a como estou criando uma Mockinstância para a resposta.
Jesse Webb

Respostas:

141

Seu mock está levantando a exceção muito bem, mas o error.resp.statusvalor está faltando. Em vez de usar return_value, basta dizer Mockque statusé um atributo:

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

Argumentos de palavra-chave adicionais para Mock()são definidos como atributos no objeto resultante.

Eu coloquei suas definições fooe barem um my_testsmódulo, adicionei na HttpErrorclasse para que eu pudesse usá-lo também, e seu teste pode ser executado com sucesso:

>>> 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

Você pode até ver a print '404 - %s' % error.messagelinha rodando, mas acho que você queria usar error.contentlá; esses são os HttpError()conjuntos de atributos do segundo argumento, de qualquer maneira.

Martijn Pieters
fonte
2
Essa side_effecté a parte principal
Daniel Butler,