In Python, mocking objects is quite easy but sometimes it can become confusing and frustrating. It needs a bit of practise and when you master it – it’s a very powerful (and a must-have) tool to make your tests better. It all works fine until you try to mock a name attribute then things get a bit weird and tricky.
Simple Mock example
This came out as I recently worked a lot with gcloud library. This is required by our own code, which means to test it we had to mock few things (actually, a lot…). Everything was going fine until I had to mock .name attributes in some classes. The very simple MagicMock(name=’foo’) won’t work as expected.
Let’s start with a simple mock example
from unittest.mock import MagicMock me = MagicMock(name='Robert', eyes='blue', hair='blond') print(me.eyes, me.hair) print(me.name)
What we do is simply creating a Mock object with some attributes. Let’s see the output:
blue blond <MagicMock name='Robert.name' id='4307997304'>
Hm, it’s not what you’d expect right? Why the name is not what we’ve defined while creating an object?
This happens because the name is an argument to the Mock constructor and you can’t just pass it in at creation time.
According to the docs:
name: If the mock has a name then it will be used in the repr of the mock. This can be useful for debugging. The name is propagated to child mocks.
Simply said name parameter is just used for easier debugging. This means we can’t just set it. There must be a way and there’s more than 1!
Setting a name after mock creation
In my opinion this is the simplest solution and all you need to do is set a name after mock creation, just like that:
from unittest.mock import MagicMock me = MagicMock(eyes='blue', hair='blond') me.name = 'Robert' print(me.eyes, me.hair) print(me.name) >>> blue blond >>> Robert
That’s it! All good.
The second approach is to use the configure_mock() method. The code looks like this:
from unittest.mock import MagicMock me = MagicMock(eyes='blue', hair='blond') me.configure_mock(name='Robert') print(me.eyes, me.hair) print(me.name) >>> blue blond >>> Robert
I find this solution as the most “magical” and don’t use it. I feel it’s a bit hacky. In this method you have to use a PropertyMock() class to attach a property to mock object. See why I think it’s not “pretty”:
from unittest.mock import MagicMock, PropertyMock me = MagicMock(eyes='blue', hair='blond') name = PropertyMock(return_value='Robert') type(me).name = name print(me.eyes, me.hair) print(me.name) >>> blue blond >>> Robert
As you can see there’s magic happening here. We’re attaching it to the mock type object.