Kay's pages

Yesterday I have implemented a test for a Python module of the efaLive project. It required me to mock out one class that is used by the test target. So I used the @patch annotation to mock the class. In a first step I used assert_called_once(), copied from some website, to verify that a specific method of the mock has been called. The test was green. Then I changed the test to use assert_called_once_with() to verify the arguments of the mock method as well. After that, the test became red. I checked the test code and did not find the reason for this. With assert_called_once() I verified that the method is called, but assert_called_once_with() tells me that it is called with other arguments than I expected. Here the test code that was green:

@patch("package1.ThirdPartyClass")
def test_my_module(self, third_party_mock)
    class_under_test = my_module.MyClass()
    class_under_test.do_something()
    third_party_mock.called_by_do_something.assert_called_once()

After a while I found an interesting article about this problem. The point is: third_party_mock is a mock object and in Python you can call any method of such a mock and it will not fail. The method assert_called_once() does not exist. It was a failure to copy this code from an article in the web. However, the method assert_called_once_with() exists and triggers the statistics of the mock object. But there is another problem in the test code. The method called_by_do_something() is never called of the mock, but of its instance. So the correct code would be:

@patch("package1.ThirdPartyClass")
def test_my_module(self, third_party_mock)
    class_under_test = my_module.MyClas()
    class_under_test.do_something()
    third_party_mock.return_value.called_by_do_something.assert_called_once_with("foo")

This code will work and trigger the correct mock statistics. To avoid issues like this, I even go a step further now. I don't use the convenience method assert_called_once_with() any more. Now I use the statistics attributes call_count and call_args directly. Here is what I would suggest to use for the test:

@patch("package1.ThirdPartyClass")
def test_my_module(self, third_party_mock)
    class_under_test = my_module.MyClas()
    class_under_test.do_something()
    self.assertEqual(1, third_party_mock.return_value.called_by_do_something.call_count)
    self.assertEqual(call("foo"), third_party_mock.return_value.called_by_do_something.call_args)