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

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:

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

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

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

Additional keyword arguments to Mock() are set as attributes on the resulting object.

I put your foo and bar definitions in a my_tests module, added in the HttpError class so I could use it too, and your test then can be ran to success:

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

You can even see the print '404 - %s' % error.message line run, but I think you wanted to use error.content there instead; that's the attribute HttpError() sets from the second argument, at any rate.

From: stackoverflow.com/q/28305406

Back to homepage or read more recommendations: