Unit Tests Return MagicMock Object Instead of return_value. How to Fix This?

adil
2 min readDec 18, 2021

When you need to create a mock object in Python, MagicMock comes in handy. However, there is a common issue that most people run into: Python returns MagicMock object instead of return_value

Suppose your method calls another method, which in turn calls another method. MagicMock can be useful for such a chained call.

Photo by Chris Ried on Unsplash

I created three classes that interact with one another:

This is how I run the code:

user = UserController()
print("---")
print(user.get_first_picture())
print("---")
print(user.get_second_picture())

I get this output when I run the code:

__init__ method in the UserController class called
__init__ method in the Picture class called
---
get_first_picture() method called
get_pictures_via_variable() method called
all() method called
Pic1
---
get_second_picture() method called
get_pictures_via_method() method called
get_DB() method called
all() method called
Pic2

Please notice that the get_first_picture method uses a variable to access the FakeDBAL class. The get_second_picture method uses get_DB() to access the FakeDBAL class

Let’s write a unit test for the get_first_picture() method:

I created a mock object for the constructor method of the UserController class. Because I didn’t want to initialize other classes (Picture, FakeDBAL) in accordance with the unit test rules.

I get this output when I run the code:

AssertionError: 'Test 1' != <MagicMock name='mock.dbal.all().__getitem__()' id='4456571856'>

Based on the output, I should create a mock object for the dbal variable. Let’s do it.

The test has passed.

Why does this unit test work fine?

Please notice that the picture variable in UserController accesses the dbal variable that is returned by the Picture object. The dbal variable has the memory address of the FakeDBAL object.

I created a mock object to match the chain in get_pictures_via_variable. The chain:

return self.picture.dbal.all()

The mock object to match this chain:

mock_app.dbal.all.return_value = ["Test 1", "Test 2"]

A unit test for the get_second_picture method

Let’s copy/paste the unit test for get_second_picture. Because these two methods are very, very similar, right?

I get this output when I run the code:

AssertionError: 'Test 2' != <MagicMock name='mock.get_DB().all().__getitem__()' id='4387991856'>

Why didn’t it work?

Because the mock object doesn’t match the chain in get_pictures_via_method:

return self.picture.get_DB().all()

So, I should replace the dbal variable with the get_DB variable in the unit test code:

I still get AssertionError when I run this unit test:

AssertionError: 'Test 2' != <MagicMock name='mock.get_DB().all().__getitem__()' id='4459012640'>

Why?

Please notice the MagicMock object’s name:

mock.get_DB().all().__getitem__()

The MagicMock’s name gives us a clue. Please notice the parenthesis next to get_DB. It means the get_DB is not a variable, it is a method.

In other words, the get_DB method will return a value then the all() method is accessible. Let’s change the unit test:

The test has passed.

--

--