模拟函数以引发异常以测试except块

时间:2015-02-03 17:46:31

标签: python unit-testing python-2.7 mocking python-mock

我有一个调用另一个函数(foo)的函数(bar)。如果调用bar()引发HttpError,我想特别处理它,如果状态代码是404,否则重新加注。

我正在尝试围绕此foo函数编写一些单元测试,模拟对bar()的调用。不幸的是,我无法通过模拟调用bar()来引发由except块捕获的异常。

这是我的代码,它说明了我的问题:

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

我按照Mock docs说明您应该将side_effect实例的Mock设置为Exception类,以使模拟函数引发错误。

我还看了一些其他相关的StackOverflow Q& As,看起来我正在做他们正在做的同样的事情,并且他们的模拟引发了异常。

为什么设置side_effect的{​​{1}}不会导致预期barMock被提升?如果我做了一些奇怪的事情,我应该如何在Exception块中测试逻辑?

1 个答案:

答案 0 :(得分:94)

你的模拟正在提升异常,但error.resp.status值丢失了。不要使用return_value,只需告诉Mock status是一个属性:

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

Mock()的其他关键字参数在结果对象上设置为属性。

我将foobar定义放在my_tests模块中,添加到HttpError class中,因此我也可以使用它,然后您的测试可以运行到成功:

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

你甚至可以看到print '404 - %s' % error.message一行,但我想你想在那里使用error.content;无论如何,这是第二个参数设置的属性HttpError()