JuanKB1024
JuanKB1024

Reputation: 344

Mock the fetchone() method of the Mysql Cursor class and set its return value to None

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?

Upvotes: 1

Views: 456

Answers (1)

User051209
User051209

Reputation: 2568

Use of patch.object() and side_effect

I have write a test which can be force fetchone() to returne None by the use of:

  • the function with mock.patch() to get the mock object which avoids the real connection to the database (exactly as in your code)
  • the function 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().

Test with the context manager

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()

Upvotes: 0

Related Questions