151

我有一个函数 ( 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 文档,其中说您应该将实例的 设置为side_effect类以使模拟函数引发错误。MockException

我还查看了其他一些相关的 StackOverflow 问答,看起来我正在做与他们正在做的事情相同的事情,以导致他们的模拟引发异常。

为什么设置side_effectbarMock导致预期Exception提高?如果我在做一些奇怪的事情,我应该如何在我的except块中测试逻辑?

4

1 回答 1

174

您的模拟可以很好地引发异常,但是error.resp.status缺少该值。而不是使用return_value,只是告诉Mockstatus是一个属性:

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

附加的关键字参数Mock()被设置为结果对象的属性。

我将你的foobar定义放在一个my_tests模块中,添加到HttpError中,这样我也可以使用它,然后你的测试就可以成功运行:

>>> 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()无论如何,这是第二个参数的属性集。

于 2015-02-03T18:07:46.517 回答