Python Unittest Examples: Mocking and Patching

Python Unittest Examples: Mocking and Patching

Last updated:
Table of Contents

TL;DR: See a working project with all examples on this github repo

Mock imported function

This is called patching.

Use patch("path.to.imported.function"):

EXAMPLE:

  • Application code (main.py)

    from myproject.helpers import complex_function
    
    # function_a is coupled to the output of complex_function
    def function_a():
        return complex_function().upper()
    
  • Test code for function_a()

    from unittest.mock import patch
    
    from myproject.main import function_a
    
    def test_function_a():
        # note that you must pass the name as it is imported on the application code
        with patch("myproject.main.complex_function") as complex_function_mock:
    
            # we dont care what the return value of the dependency is
            complex_function_mock.return_value = "foo"
    
            # we just want our function to work
            assert function_a() == "FOO"
    

Patch object of class

Use patch.object(MyClass, 'my_method', return_value="return value"):

EXAMPLE:

  • application code:

    class MyClass:
        def __init__(self, name):
            self.name = name
    
        def sayhi(self):
            return "hi my name is: {}".format(self.name)
    
    # instantiates MyClass and calls a method on the object
    def function_b():
        param1 = MyClass("foo")
    
        # returns "hi my name is: foo"
        return param1.sayhi()
    
  • test code

    from unittest.mock import patch
    
    def test_function_b():
    
        # mock an object of class 
        with patch.object(MyClass, 'sayhi', return_value="hi i'm a mock object"):
    
            # the MyClass object used within function_b will
            # be replaced by a mock defined in the
            # patch.object call above
            assert function_b() == "hi i'm a mock object"
    

Mocked method is called

Use assert_not_called() to assert a method was not called instead

To assert that a mocked method was called use assert_called()

EXAMPLE:

from unittest.mock import Mock

# this function takes an object as argument and calls a
# method on it
def function_with_call(some_obj, argument):
    return some_obj.some_method(argument)

def test_called():
    mock = Mock()
    mock.some_method = Mock(return_value=None)

    function_with_call(mock, "foo bar")

    # will return true if method was called one or more times
    mock.some_method.assert_called()

Patched function is called

Use of assert_not_called() to assert it was not called

Use patch(...) and assert_called() to assert that a pacthed function was called.

EXAMPLE:

  • Application code (main.py)

    from myproject.helpers import complex_function
    
    def function_a():
        return complex_function().upper()
    
  • Test code:

    from unittest.mock import patch
    
    from myproject.main import function_a
    
    def test_called_patch():
        with patch("myproject.main.complex_function") as patched_function:
            function_a()
    
        patched_function.assert_called()
    

Mocked method is called with arguments

Use assert_called_with(arg1, arg2, key1=kwarg1,key2=kwarg2,...) to verify that the method was called using the arguments provided:

EXAMPLE:

from unittest.mock import Mock


def function_with_call(some_obj, argument):
    return some_obj.some_method(argument)


def test_called():
    mock = Mock()
    mock.some_method = Mock(return_value=None)

    function_with_call(mock, "foo bar")

    # will return true if method was called one or more times
    mock.some_method.assert_called_with("foo bar")

Patched function is called with arguments

Use patch(...) and assert_called_with()

EXAMPLE:

  • application code (main.py)

    from myproject.helpers import complex_function_with_params
    
    # this function takes both params, modifies them and send to 
    # complex_function_with_params
    def function_e(param1, param2):
        return complex_function_with_params(param1.upper(), param2.upper())
    
  • test code

    from unittest.mock import patch
    
    from myproject.main import function_e
    
    def test_called_with_patch():
        with patch("myproject.main.complex_function_with_params") as patched_function:
            # function_e converts the params to upper case and
            # calls another function with those
            function_e("foo", "bar")
    
        patched_function.assert_called_with("FOO", "BAR")
    

Mock object method

Create a Mock() object and assign another Mock object to the method name

EXAMPLE:

from unittest.mock import Mock

# function_c takes a parameter and calls .sayhi() on it
def function_c(param):
    output = param.sayhi()

    return output.upper()


def test_function_c():

    mock_obj = Mock()
    mock_obj.sayhi = Mock(return_value="foo")

    # function_c takes as input a MyClass object, calls sayhi() on it
    # and makes the result upper case
    assert function_c(mock_obj) == "FOO"

Mock object attribute

Create a Mock() object and assign stuff to attribute names:

EXAMPLE:

  • application code

    # function_d takes a parameter and uses its .name attribute
    def function_d(param):
        output = param.name
    
        return output.upper()
    
  • test code

    from unittest.mock import Mock
    
    def test_function_d():
    
        mock_obj = Mock()
        mock_obj.name = "foo"
    
        assert function_d(mock_obj) == "FOO"
    

Mock chained method calls

The same can be done for patched objects

Use Mock and assign to methods' return_value:

EXAMPLE:

from unittest.mock import Mock

# this function calls a chain of methods on the given object
def my_function(some_object):

    output = some_object.foo().bar.baz().quux()

    return output.upper()


def test_chained():
    mock_obj = Mock()

    result_1 = mock_obj.foo.return_value
    result_2 = result_1.bar.baz.return_value
    result_2.quux.return_value = "hello world"

    assert my_function(mock_obj) == "HELLO WORLD"

Patch open file

Lots more info on how to work with files here

Use patch() with mock_open():

EXAMPLE:

  • application code (main.py)

    # reads a file and converts the contents to upper case
    def file_contents_to_uppercase(path_to_file):
        with open(path_to_file, "r") as f:
            contents = f.read()
    
            return contents.upper()
    
  • test code

    from unittest.mock import mock_open, patch
    
    from myproject.main import file_contents_to_uppercase
    
    def test_file_contents_to_upper_case():
        # pass the desired content as parameter
        m = mock_open(read_data="foo bar")
    
        with patch('myproject.main.open', m):
            # it does not matter what file path you pass,
            # the file contents are mocked
            assert file_contents_to_uppercase("path/to/file") == "FOO BAR"
    

Get arguments passed to mocked method

Use mocked_method.call_args[0]

EXAMPLE:

from unittest.mock import Mock

def function_e(some_obj, argument):
    return some_obj.some_method(argument)

def test_arguments():

    mock = Mock() # mock object
    mock.some_method = Mock(return_value=None) # mock method

    function_e(mock, "foo bar")

    mock.some_method.assert_called()

    # arguments passed to method some_method
    arguments_tuple = mock.some_method.call_args[0]

    assert "foo bar" in arguments_tuple

Mock vs Patch

mock patch
Used to replace something that
is used in the current scope
Used to replace something that is imported
and/or created in another scope

References

Dialogue & Discussion