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
?
CodePudding user response:
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.