Python Unit Testing Mock

Python Mock

mock has become part of the standard library, and is imported as unittest.mock in Python 3.6+. mock is used to replace parts of your system under test with mock objects and make assertions about how they have been used.

Using the patch() decorator, you are able to replace objects in your testing code with mock objects, the objects are searched in the namespace where the decorator is used. Be cautious about the Order of the decorators, the patching is done from the bottom upwards.

In the below example, while executing the test_function(), the read_csv, write_csv and os.path.exists are replaced with mock objects, and returns the values we specify. The actual functions are not executed.

from unittest.mock import patch
import os
import pandas as pd


def target_func(filepath):
    if os.path.exists(filepath):
        df = pd.read_csv("fake.csv")
        df.to_csv("foo.csv")
        return "File path is found and CSVs are read and wrote."
    else:
        return "Not Found"


@patch('pandas.read_csv')
@patch('pandas.DataFrame.to_csv')
@patch('os.path.exists')
def test_func(mock_path_exists, mock_df_to_csv, mock_read_csv):
    mock_read_csv.return_value = pd.DataFrame()
    mock_path_exists.return_value = True
    res = target_func('sth')
    print(res)
    
test_func()
File path is found and CSVs are read and wrote.

Another cleaner way to use mock is to replace the objects in the decorator itself. By defining a gerneral object, no matter how the actual functions are called, we ignore the inputs and return the values we specify. In this case, to_csv does not even have a RANDOM_PARAM argument, but the code doesn’t fail.

from unittest.mock import patch
import os
import pandas as pd


def Mock_None(*args, **kwargs):
    return None


def target_func():
    return pd.read_csv(), pd.DataFrame.to_csv(RANDOM_PARAM=False), os.path.exists()


# Mock_None is a function (object), which is used as the mock object here
@patch('pandas.read_csv', Mock_None)
@patch('pandas.DataFrame.to_csv', Mock_None)
@patch('os.path.exists', Mock_None)
def test_func():
    res = target_func()
    print(res)
test_func()
(None, None, None)
Yiming Zhang
Yiming Zhang
Quantitative Researcher Associate, JP Morgan