The method doesn’t exist and therefore the return value is an instance of Mock. An instance of mock evaluates to true, which makes this a tautology test and thus gives a false sense of safety.
See this short snippet of the passing assertion:
In [1]: from unittest import mock
In [2]: m = mock.Mock()
In [3]: m.method(param=1)
Out[3]: <Mock name='mock.method()' id='4353968160'>
In [4]: m.method.called_once_with(arg=2)
Out[4]: <Mock name='mock.method.called_once_with()' id='4387616944'>
In [5]: assert m.method.called_once_with(arg=2)
When in fact the test should fail:
In [14]: m.method.assert_called_once_with(arg=2)
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Cell In [14], line 1
----> 1 m.method.assert_called_once_with(arg=2)
File /opt/local/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/unittest/mock.py:941, in NonCallableMock.assert_called_once_with(self, *args, **kwargs)
936 msg = ("Expected '%s' to be called once. Called %s times.%s"
937 % (self._mock_name or 'mock',
938 self.call_count,
939 self._calls_repr()))
940 raise AssertionError(msg)
--> 941 return self.assert_called_with(*args, **kwargs)
File /opt/local/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/unittest/mock.py:929, in NonCallableMock.assert_called_with(self, *args, **kwargs)
927 if actual != expected:
928 cause = expected if isinstance(expected, Exception) else None
--> 929 raise AssertionError(_error_message()) from cause
AssertionError: expected call not found.
Expected: method(arg=2)
Actual: method(param=1)
The IDLE test, being fixed by gh_100647, is the only one in the stdlib. So I am not convinced that this is needed. If you have data that this mistake is near as common as checking for ‘assert’ misspelled as ‘assret’, it would a plausible addition.
Yes, there was only one occurrence. Compared to 0 for each of ‘assert’, ‘assret’, ‘asert’, ‘aseert’, ‘assrt’ in the stdlib. In my opinion, it’s much more likely to make this mistake than one of the typos, because it looks correct and there could be code like
A quick search on github shows some results and in our private (multi million line) code base, I found more than 500 across all repositories, with more than half of it not passing after fixing the call from assert mock_f.called_once_with to mock_f.assert_called_once_with().
The mock module already does a good job to protect against trivial typos, but I think it could even do a better job protecting against this error class. The functionality already exists and just needs to be extended. As I’ve written in the initial post, I already have a patch, which correctly detected the wrong assertion in idle.
Instead of the prefix called_, why not all the known methods and attributes with prefix assert_ removed? That allows any real code using that prefix (eg called_by_first_name in some hypothetical international people-management library) to not fail the test.
In that case, I think it would make more sense to have a denylist of names rather than using prefixes. I just updated the pull request.
Note that according to the error message one should be able to provide a spec for their mock object, but this seems to be broken, as commented by myself on the PR.