Recently I changed a function to receive a single argument instead of the previous two. Unfortunately that resulted in another function failing later. The issue wasn’t lack of tests (that function had 100% coverage), but the way this function was tested.
@patch('module.function_changed') def test_that_function_was_called(mock_function): task() mock_function.assert_called_with('arg1', 'arg2')
One of the downsides of using mock objects in tests is that you lose the connection to the original object. So the code above runs happily and the test passes because mock_function is a simple Mock object. But we don’t need to accept this, a simple change will make the Mock object validate that the arguments are valid for the original functional:
@patch('module.function_changed', autospec=True) def test_that_function_was_called(mock_function): task() mock_function.assert_called_with('arg1', 'arg2')
autospec=True to the
patch call, the arguments are validated against the original function signature, and in this case the call would raise a
TypeError: function_changed() takes 1 positional argument but 2 were given and the test would fail. And now you’d know that something was wrong well before it gets deployed…