Pitfalls with mocking in Python
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 I 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_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() assert third_party_mock.return_value.called_by_do_something.call_count == 1 assert third_party_mock.return_value.called_by_do_something.call_args == (("foo"),)
Web pages update
I have updated my web pages, the layout and the CMS, as you might have recognized already. The layout works responsive now. This was one of my main goals. Besides that I simplified the layout so that there are less graphics. This hopefully reduces the page load and loading time. So the pages should be ready for the mobile world. The only part that does not work responsive completely is the gallery. The details view does not resize. I hope that I will find a solution for that. The content is more or less the same as before.
Now have fun on the pages!
efaLive 2.2 released!
Today I have released efaLive version 2.2. There are many structural changes but some new features as well. For more information check the efaLive page.
Microsoft's hand made parser
I recently had to implement an API to send push messages to Windows phone 8 from Java. So called "tile" messages did work fine after getting the authentication part working. But "toast" messages did not work. The Microsoft push server answered that everything is OK, but the test software on the phone said that there is an error in the payload. The payload for these messages is XML which describes to the phone what text etc it should display.
After some conversation with Microsoft everything pointed to the encoding of the XML payload. I was wondering, why they use different encoding for tile and toast messages. After some checks in the code, I was 100% sure that I send UTF8 there as required.
Then I took a test push send software from Microsoft and analyzed the payload. There were some different HTTP headers, but the payload was XML just as mine. The only difference was, that the Microsoft payload was formatted XML. And yes, unbelievable, but this was the problem. After I added carriage return/line feed and some spaces, the toast messages worked as expected.
Thanks Microsoft for this nice hand made XML parser that expects the payload to contain "\r\n" to form a well formatted XML string!! And I thought that formatting XML is only good for human being's eyes. You never stop learning...
Source of efaLive now on GitHub!!
The source code of everything that makes up efaLive is now available on GitHub. Up to now, the source code was available in the CD image only. There was no configuration for live-build to create CD images. Now, everything is available to create an efaLive CD image. All Bash scripts, Python code and configuration files are there. It took some time to make it available in such a comfortable way. I had to restructure many things so that it is more easy for "external" people to understand the project. See the efaLive page for more information.