模拟函数以引发异常以测试 except 块
Mocking a function to raise an Exception to test an except block
我有一个函数 (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 说你应该将 Mock
实例的 side_effect
设置为 Exception
class 以具有模拟功能引发错误。
我还查看了其他一些相关的 Whosebug 问答,看起来我正在做他们正在做的同样的事情,以导致他们的模拟引发异常。
- How to use Python Mock to raise an exception - but with Errno set to a given value
为什么设置 barMock
的 side_effect
不会导致预期的 Exception
升高?如果我在做一些奇怪的事情,我应该如何在 except
块中测试逻辑?
您的模拟可以很好地引发异常,但缺少 error.resp.status
值。不要使用 return_value
,只需告诉 Mock
status
是一个属性:
barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')
Mock()
的其他关键字参数被设置为结果对象的属性。
我把你的 foo
和 bar
定义放在 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()
。
我有一个函数 (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 说你应该将 Mock
实例的 side_effect
设置为 Exception
class 以具有模拟功能引发错误。
我还查看了其他一些相关的 Whosebug 问答,看起来我正在做他们正在做的同样的事情,以导致他们的模拟引发异常。
- How to use Python Mock to raise an exception - but with Errno set to a given value
为什么设置 barMock
的 side_effect
不会导致预期的 Exception
升高?如果我在做一些奇怪的事情,我应该如何在 except
块中测试逻辑?
您的模拟可以很好地引发异常,但缺少 error.resp.status
值。不要使用 return_value
,只需告诉 Mock
status
是一个属性:
barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')
Mock()
的其他关键字参数被设置为结果对象的属性。
我把你的 foo
和 bar
定义放在 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()
。