Hard to understand return_value for Python Mock Class in the following example

Question:

I had a hard time understanding the choice of using return_value. See the following example

# my_module.py

def query(connection):
    cursor = connection.cursor()
    cursor.execute("SELECT name FROM users WHERE id = 1")
    return cursor.fetchone()[0]
# test_query.py

import pytest
from unittest.mock import Mock

# Import the module containing the function to test
import my_module

def test_query():
    # Set up the mock database connection
    connection = Mock()
    cursor = connection.cursor.return_value
    cursor.fetchone.return_value = ("Alice",)

    # Call the function under test
    result = my_module.query(connection)

    # Assert that the function has the expected behavior
    assert result == "Alice"

My question is in test_query.py, why do I need the first return_value? It looks like if cursor = connection.cursor.return_value, I can replace the cursor in cursor.fetchone.return_value = ("Alice",), which then becomes connection.cursor.return_value.fetchone.return_value = ("Alice",)? Why does this code require two return_value ?

Without the first return_value, I received TypeError: 'Mock' object is not subscriptable after running pytest

The broader question is when should I use return_value?

Asked By: Bratt Swan

||

Answers:

First lets make sure we understand what Mock is. You can think of it as an object that’s every attribute or function call returns another object of type Mock.

from unittest.mock import Mock

m = Mock()
for cls in [m, m.any_attribute, m.any_function("any_param"), m.any_function.return_value]:
    assert isinstance(cls, Mock)

We can easilly implement such behaviour by ourself:

class MyMock:
    def __getattribute__(self, _: str):
        return MyMock()

    def __call__(self, *args, **kwargs):
        return MyMock()

However return_value has a special meaning. With return value you ask for (or set by yourself) result of calling specified function.

Now, what our goal is? Our goal is to make cursor.fetchone() function return something that is subscriptable (because we ask for first element with [0]). But with such syntax:

connection.cursor.fetchone.return_value = ("Alice, )

We are mocking result of calling fetchone from connection.cursor, not connection.cursor(). It would work fine only if cursor is a property.

To sum up, whenever you see a function call you use return_value and in case of attributes you don’t.

Answered By: kosciej16