I'm trying to make a MagicMock instance of the mysql connector, but I need for the method fetchone()
to return None.
This is what I've done so far:
with mock.patch('mysql.connector.cursor') as dbmock, \
mock.patch('mysql.connector.connect', mock.MagicMock()):
dbcursor_mock = dbmock.return_value # Get the mock for the cursor
dbcursor_mock.fetchone.return_value = None # Set the return value of fetchone
The problem is that this returns a MagicMock
instance and not None
.
Now, if I remove the second patch()
, it does work:
with mock.patch('mysql.connector.cursor') as dbmock):
dbcursor_mock = dbmock.return_value
dbcursor_mock.fetchone.return_value = None # this does return None, Why?
But then my code will try to connect to the db and fail.
I'm using the MySQL cursor within a context manager like this:
def fetch_a_row():
# establishing the connection
with mysql.connector.connect(user='root',password='password',host='127.0.0.1',database='mydb') as conn:
# Creating a cursor object using the cursor() method
cursor = conn.cursor()
cursor.execute("SELECT * FROM mytable")
# return the fetchone() output
return cursor.fetchone()
How can I make an instance of MagicMock return None?
I have write a test which can be force fetchone()
to returne None
by the use of:
with mock.patch()
to get the mock object which avoids the real connection to the database (exactly as in your code)with mock.path.object()
to mock the method cursor()side_effect
instead of return_value
to force fetchone()
to return None
A test adapt for your need should be the following:
import unittest
from unittest import mock
import mysql.connector
def fetch_a_row():
# establishing the connection
conn = mysql.connector.connect(user='root',password='password',host='127.0.0.1',database='mydb')
# Creating a cursor object using the cursor() method
cursor = conn.cursor()
# return the fetchone() output
return cursor.fetchone()
class TestMysqlConn(unittest.TestCase):
def test_01(self):
# following patch() is the same you have used in your code
with mock.patch('mysql.connector.connect') as mock_connect:
mock_connect_instance = mock_connect.return_value
# mock the method cursor() of the mock object mock_connect
with mock.patch.object(mock_connect_instance, 'cursor') as mock_cursor:
mock_cursor_instance = mock_cursor.return_value
# force fetchone() to return None by side_effect
mock_cursor_instance.fetchone.side_effect = [None]
# check if fetch_a_row() return None
self.assertEqual(None, fetch_a_row())
if __name__ == '__main__':
unittest.main()
In the code I have written the function fetch_one_row()
which simulate your production code.
EDIT: I have edited the answer because the OP have add his code for the function fetch_a_row()
.
You have edited your question and add the code of the function fetch_a_row()
with the use of the context manager.
When you use a context manager is called the method __enter__()
(see this link) and this method returns the object conn
. So with context manager I have to modify the test function as following:
import unittest
from unittest import mock
import mysql.connector
def fetch_a_row():
# establishing the connection
with mysql.connector.connect(user='root',password='password',host='127.0.0.1',database='mydb') as conn:
# Creating a cursor object using the cursor() method
cursor = conn.cursor()
cursor.execute("SELECT * FROM mytable")
# return the fetchone() output
return cursor.fetchone()
class TestMysqlConn(unittest.TestCase):
def test_01(self):
with mock.patch('mysql.connector.connect') as mock_connect:
mock_connect_instance = mock_connect.return_value
with mock.patch.object(mock_connect_instance, '__enter__') as mock_connect_context_manager:
mock_connect_context_manager_instance = mock_connect_context_manager.return_value
with mock.patch.object(mock_connect_context_manager_instance, 'cursor') as mock_cursor:
mock_cursor_instance = mock_cursor.return_value
mock_cursor_instance.fetchone.side_effect = [None]
self.assertEqual(None, fetch_a_row())
if __name__ == '__main__':
unittest.main()