Search code examples
pythonunit-testingexceptionattributeerrorpython-mock

python - assert_called_with where AttributeError is passed as arg


I am trying to unit test a custom exception called TargetException.

One of the arguments to this exception is itself an exception.

Here's the relevant part of my test:

mock_exception.assert_called_once_with(
    id,
    AttributeError('invalidAttribute',)
)

Here's the test failure message:

  File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 948, in assert_called_once_with
    return self.assert_called_with(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 937, in assert_called_with
    six.raise_from(AssertionError(_error_message(cause)), cause)
  File "/usr/local/lib/python2.7/site-packages/six.py", line 718, in raise_from
    raise value
AssertionError: Expected call: TargetException(<testrow.TestRow object at 0x7fa2611e7050>, AttributeError('invalidAttribute',))
Actual call: TargetException(<testrow.TestRow object at 0x7fa2611e7050>, AttributeError('invalidAttribute',))

In both the "Expected call" and the "Action call," the same arguments are present -- at least it appears that way to me.

Do I need to pass the AttributeError a different way to resolve the error?


Solution

  • The problem is that you compare the instances of the contained exception. As the AttributeError instance created in the tested function and the instance used for comparison in test are different, the assertion fails.

    What you could do instead is testing the called arguments separately to ensure they are of the correct type:

    @mock.patch('yourmodule.TargetException')
    def test_exception(mock_exception):
        # call the tested function
        ...
        mock_exception.assert_called_once()
        assert len(mock_exception.call_args[0]) == 2  # shall be called with 2 positional args
        arg1 = mock_exception.call_args[0][0]  # first argument
        assert isinstance(arg1, testrow.TestRow)  # type of the first arg
        ... # more tests for arg1
    
        arg2 = mock_exception.call_args[0][1]  # second argument
        assert isinstance(arg2, AttributeError)  # type of the second arg
        assert str(arg2) == 'invalidAttribute'  # string value of the AttributeError
    

    E.g. you test for the class and for the relevant values of the arguments separately. Using assert_called_with works only for PODs, or if you already know the called instance (for example if it is a singleton, or a known mock).