关于python:模拟一个函数来引发一个Exception来测试一个except块

Mocking a function to raise an Exception to test an except block

我有一个函数(foo调用另一个函数(bar)。如果调用bar()引发HttpError,我希望在状态代码为404时特别处理,否则重新引发。

我试图围绕这个foo函数编写一些单元测试,模拟对bar()的调用。不幸的是,我无法得到对bar()的模拟调用,以引发由我的except块捕获的异常。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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设置为Exception类,以使模拟函数产生错误。

我还查看了一些其他相关的stackoverflow q&as,看起来我正在做与它们所做的相同的事情,它们的模拟会引发异常。

  • https://stackoverflow.com/a/10310532/346561
  • 如何使用python mock引发异常-但将errno设置为给定值

为什么设置barMockside_effect不引起预期的Exception升高?如果我在做一些奇怪的事情,我应该如何在我的except块中测试逻辑?


您的模拟会引发异常,但缺少error.resp.status值。不要使用return_value,只要告诉Mockstatus是一个属性:

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

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

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

1
2
3
4
5
6
7
8
9
>>> 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()集的属性,无论如何。