Source From Here
Question
I have a function (foo) which calls another function (bar). If invoking bar() raises an HttpError, I want to handle it specially if the status code is 404, otherwise re-raise. I am trying to write some unit tests around this foo function, mocking out the call to bar(). Unfortunately, I am unable to get the mocked call to bar() to raise an Exception which is caught by my except block.
Here is my code which illustrates my problem:
- test.py
Execution output:
# pytest -s -v test.py
I followed the Mock docs which say that you should set the side_effect of a Mock instance to an Exception class to have the mocked function raise the error. I also looked at some other related StackOverflow Q&As, and it looks like I am doing the same thing they are doing to cause and Exception to be raised by their mock.
Why is setting the side_effect of bar Mock not causing the expected Exception to be raised? If I am doing something weird, how should I go about testing logic in my except block?
How-To
Your mock is raising the exception just fine, but the error.resp.status value is missing. Rather than use return_value, just tell Mock that status is an attribute:
Additional keyword arguments to
Mock() are set as attributes on the resulting object. The modified test.py will look like:
The execution output:
Question
I have a function (foo) which calls another function (bar). If invoking bar() raises an HttpError, I want to handle it specially if the status code is 404, otherwise re-raise. I am trying to write some unit tests around this foo function, mocking out the call to bar(). Unfortunately, I am unable to get the mocked call to bar() to raise an Exception which is caught by my except block.
Here is my code which illustrates my problem:
- test.py
- import unittest
- from unittest import mock
- class HttpError(Exception):
- def __init__(self, result, msg):
- super(HttpError, self).__init__(msg)
- self.result = result
- class FooTests(unittest.TestCase):
- @mock.patch('test.bar')
- def test_foo_shouldReturnResultOfBar_whenBarSucceeds(self, barMock):
- barMock.return_value = True
- result = foo()
- self.assertTrue(result) # passes
- @mock.patch('test.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('test.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()
# pytest -s -v test.py
I followed the Mock docs which say that you should set the side_effect of a Mock instance to an Exception class to have the mocked function raise the error. I also looked at some other related StackOverflow Q&As, and it looks like I am doing the same thing they are doing to cause and Exception to be raised by their mock.
Why is setting the side_effect of bar Mock not causing the expected Exception to be raised? If I am doing something weird, how should I go about testing logic in my except block?
How-To
Your mock is raising the exception just fine, but the error.resp.status value is missing. Rather than use return_value, just tell Mock that status is an attribute:
- barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')
- import unittest
- from unittest import mock
- class HttpError(Exception):
- def __init__(self, resp, msg):
- super(HttpError, self).__init__(msg)
- self.resp = resp
- self.message = msg
- class FooTests(unittest.TestCase):
- @mock.patch('test.bar')
- def test_foo_shouldReturnResultOfBar_whenBarSucceeds(self, barMock):
- barMock.return_value = True
- result = foo()
- self.assertTrue(result) # passes
- @mock.patch('test.bar')
- def test_foo_shouldReturnNone_whenBarRaiseHttpError404(self, barMock):
- barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')
- result = foo()
- self.assertIsNone(result) # fails, test raises HttpError
- @mock.patch('test.bar')
- def test_foo_shouldRaiseHttpError_whenBarRaiseHttpErrorNot404(self, barMock):
- barMock.side_effect = HttpError(mock.Mock(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()
沒有留言:
張貼留言