unittest.mock.Mock

Create a Mock object

[1]:
from unittest import mock
m = mock.Mock()
## Define Mock object behavior

Set a default return value

[2]:
m.return_value = 42
m()
[2]:
42

Assign different return values for successive calls

side_effect must be of type list. Each time the mocked method is then called, the next value in the list is returned. If the side_effect list has no more values to return, the next call will fail with a StopIteration error.

The return_value is like a constant side_effect, which always returns the same value.

Have a Mock return string values when called

[3]:
m.side_effect = ['foo', 'bar', 'baz']
print(m())
print(m())
print(m())
foo
bar
baz

Have a Mock returns a new Mock for each function call

[4]:
number = 10
m = mock.Mock()
m.get_mock.side_effect = [mock.Mock() for _ in range(number)]

print(m.get_mock())
print(m.get_mock())
# may continue up to 'number'-times
<Mock id='4444113184'>
<Mock id='4442458784'>

Check number of calls on Mock object

Check whether mock object has been called at least once

[5]:
m = mock.Mock()
m()
m.assert_called()

Returns None, if called, otherwise raises AssertionError

Check whether mock object has been called exactly once

[6]:
m = mock.Mock()
try:
    m.assert_called_once()
except AssertionError:
    print("No, I wasn't called (yet)")
m()
if not m.assert_called_once():
    print("Yeah, now I was called")
No, I wasn't called (yet)
Yeah, now I was called

Get number of calls to mock object

[7]:
m = mock.Mock()
m()
m()
m.call_count
[7]:
2

Get Mock call arguments

Get call arguments from most recent Mock call

[8]:
m = mock.Mock()
m(1, foo='bar')
m.call_args
[8]:
call(1, foo='bar')

If mock object hasn’t been called yet, None is returned.

Get call arguments of all previous Mock calls

[9]:
m = mock.Mock()
m()
m(1, foo='bar')
m(4, baz='bar2')
m.call_args_list
[9]:
[call(), call(1, foo='bar'), call(4, baz='bar2')]

Reset previous Mock calls (won’t change mock configuration)

[10]:
m = mock.Mock()
m()
m(1, foo='bar')
m(4, baz='bar2')
m.reset_mock()
m.call_args_list
[10]:
[]

unittest.mock.patch

Patch an import module

unittest.mock is able to patch imports in the module under test using the patch function.

patch will intercept import statements identified by a string and return a Mock instance you can preconfigure using the techniques from unittest.Mock (see above).

Module under test (work.py):

[11]:


def work_on(): path = os.getcwd() print(f'Working on {path}') return path

The project module imports os and uses its getcwd() method , which we want to mock in our test.

Patch using a context manager (with statement)

When using the context manager patch, the patch ends when the with statement ends.

[12]:
import unittest
from unittest import TestCase


class TestWorkMockingModule(TestCase):

    def test_using_context_manager(self):
        with mock.patch('work.os') as mocked_os:
            work_on()
            mocked_os.getcwd.assert_called_once()

unittest.main(argv=[''], verbosity=2, exit=False)

test_using_context_manager (__main__.TestWorkMockingModule.test_using_context_manager) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.002s

OK
Working on <MagicMock name='os.getcwd()' id='4448422176'>
[12]:
<unittest.main.TestProgram at 0x108f095e0>

We specifically patch the work.os module, not os, as this would patch os for all modules, not just for work.py.

  • The test module imports the project module’s method work_on()

  • At the beginning of the test, the work.os module (which is the os module in our work.py module) is patched with a MagickMock object (here called mocked_os)

  • When the work_on() method is called afterward, that MagicMock object is called instead of the original module (os) - as no return value is defined work_on() returns another MagicMock object - as we are not testing the correctness of the return value here, only checking if the mocked_os executed the getcwd()method, we do not care about the return value

The as statement in the decorator is optional. You may as well not mock the entire work.os module, but rather define a return value for the single function, that is called (getcwd()):

[13]:
from unittest import TestCase, main


class TestWorkMockingModule(TestCase):
    def test_using_return_value(self):
        with mock.patch('work.os.getcwd', return_value='testing'):
            assert work_on() == 'testing'

main(argv=[''], verbosity=2, exit=False)
test_using_return_value (__main__.TestWorkMockingModule.test_using_return_value) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.002s

OK
Working on testing
[13]:
<unittest.main.TestProgram at 0x109237710>

Patch using the decorator (@patch)

To test project classes that interact with other project classes in isolation, the other project classes must be mocked (in order to determine test failure to a specific class and method, not by any of its dependency classes).

Example:

[14]:
from unittest import TestCase, mock, main

from source.reference.python.cheat_sheets._file.work import work_on

class TestWorkMockingModule(TestCase):

    @mock.patch('work.os')
    def test_using_decorator(self, mocked_os):
        work_on()
        mocked_os.getcwd.assert_called_once()

main(argv=[''], verbosity=2, exit=False)
test_using_decorator (__main__.TestWorkMockingModule.test_using_decorator) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.002s

OK
Working on <MagicMock name='os.getcwd()' id='4448619744'>
[14]:
<unittest.main.TestProgram at 0x1092915b0>

Here the work.os module is replaced with a MagicMock object.

Patching classes

To test project classes that interact with other project classes in isolation, the other project classes must be mocked (in order to determine test failure to a specific class and method, not by any of its dependency classes).

Example:

Project module (worker.py)

In this module, the Worker class needs to be tested for these two things:

  • the Worker calls Helper with db

  • the Worker returns the expected path supplied by Helper

[15]:



class Helper: def __init__(self, path): self.path = path def get_path(self): base_path = os.getcwd() return os.path.join(base_path, self.path) class Worker: def __init__(self): self.helper = Helper('db') def work(self): path = self.helper.get_path() print(f'Working on {path}') return path

Test module (test_worker.py)

Patch entire class

To test this, the Worker needs to be isolated from Helper, so that failures can be associated with the Worker class only.

Consequently, the entire Helper class must be patched:

```python from unittest import TestCase, mock from worker import Worker

[16]:
from unittest import TestCase, main


class TestWorkerModule(TestCase):

    def test_patching_class(self):
        with mock.patch('worker.Helper') as MockHelper:
            MockHelper.return_value.get_path.return_value = 'testing'
            worker = Worker()
            MockHelper.assert_called_once_with('db')
            self.assertEqual(worker.work(), 'testing')

main(argv=[''], verbosity=2, exit=False)
test_using_decorator (__main__.TestWorkMockingModule.test_using_decorator) ... ok
test_patching_class (__main__.TestWorkerModule.test_patching_class) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.003s

OK
Working on <MagicMock name='os.getcwd()' id='4447399248'>
Working on testing
[16]:
<unittest.main.TestProgram at 0x1091a6e40>

Note the double return_value in the example, simply using MockHelper.get_path.return_value would not work since in the code we call get_path on an instance, not the class itself.

Alternatively, we can preconfigure the MockHelper instance and assign it as the return value of the worker.Helper call. Below, shown for both the context manager and the decorator way:

[18]:
from unittest import TestCase, mock, main


class TestWorkerModule(TestCase):

    mockHelperInstance = mock.MagicMock()
    mockHelperInstance.get_path.return_value = 'testing'

    # using patch context manager
    def test_patch_class_context_manager(self):
        with mock.patch('worker.Helper',
                        return_value=self.mockHelperInstance) as MockHelper:
            worker = Worker()
            MockHelper.assert_called_once_with('db')
            self.assertEqual(worker.work(), 'testing')

    # using patch class decorator
    @mock.patch('worker.Helper', return_value=mockHelperInstance)
    def test_patch_class_decorator(self, mock_helper):
        worker = Worker()
        mock_helper.assert_called_once_with('db')
        self.assertEqual(worker.work(), 'testing')

main(argv=[''], verbosity=2, exit=False)
test_using_decorator (__main__.TestWorkMockingModule.test_using_decorator) ... ok
test_patch_class_context_manager (__main__.TestWorkerModule.test_patch_class_context_manager) ... ok
test_patch_class_decorator (__main__.TestWorkerModule.test_patch_class_decorator) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.005s

OK
Working on <MagicMock name='os.getcwd()' id='4448099488'>
Working on testing
Working on testing
[18]:
<unittest.main.TestProgram at 0x109289550>

Important

It is important to distinguish between mocking a class and mocking the instance of a class. Sometimes, it is necessary to mock the entire class, sometimes it might be sufficient to mock only an instance from that class.

Mocking the entire class allows for

* checking, in what way that class has been initialized
* instantiating multiple instances from it, which return a Mock object and
  raises the call count

For the above worker.py module, both these tests pass:

[19]:
from unittest import TestCase, mock, main

mockHelper = mock.MagicMock()


class TestWorkerModule(TestCase):

    def setUp(self):
        mockHelper.reset_mock()
        self.mock_helper_instance = mockHelper.return_value

    @mock.patch('worker.Helper', new=mockHelper)
    def test_initialization(self):
        Worker()
        mockHelper.assert_called_once_with('db')

    @mock.patch('worker.Helper', new=mockHelper)
    def test_get_path(self):
        self.mock_helper_instance.get_path.return_value = 'testing'
        worker = Worker()
        self.assertEqual(worker.work(), 'testing')

main(argv=[''], verbosity=2, exit=False)
test_using_decorator (__main__.TestWorkMockingModule.test_using_decorator) ... ok
test_get_path (__main__.TestWorkerModule.test_get_path) ... ok
test_initialization (__main__.TestWorkerModule.test_initialization) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.005s

OK
Working on <MagicMock name='os.getcwd()' id='4448654336'>
Working on testing
[19]:
<unittest.main.TestProgram at 0x109289220>

Speccing

The danger with mocks is that a Mock object returns a new MagicMock object to basically every attribute or method it is queried for, no matter if that attribute or method actually exists. If the mocked class changes signatures or an attribute, the Mockobject will still return the requested “thing” as another Mock, simply said

The Mock does not know about the object it is mocking.

To prevent this speccing can be added to a Mock, which makes it behave as the object being mocked. The easiest to use is autospec=True option.

Example:

Assuming the Helper class changed its get_path() method to get_folder(), but the call is not changed in the Worker class:

[ ]:
import os


class Helper:

    def __init__(self, path):
        self.path = path

    def get_folder(self):
        base_path = os.getcwd()
        return os.path.join(base_path, self.path)


class Worker:

    def __init__(self):
        self.helper = Helper('db')

    def work(self):
        path = self.helper.get_path()
        print(f'Working on {path}')
        return path

When running the previous test without the speccing option, this would pass, as we mock the get_path method for the Mock instance although the original class does not contain this method anymore.

But with the autospec=True method, the Mockget_path() method, the Mock object checks for consistence with the original object. So

[25]:
from unittest import TestCase, mock, main

mockHelper = mock.MagicMock()


class TestWorkerModuleRaisesError(TestCase):
    @mock.patch('worker.Helper', new=mockHelper, autospec=True)
    def test_get_path(self):
        self.mock_helper_instance.get_path.return_value = 'testing'
        worker = Worker()
        self.assertEqual(worker.work(), 'testing')

main(argv=[''], verbosity=0, exit=False)
======================================================================
ERROR: test_get_path (__main__.TestWorkerModuleRaisesError.test_get_path)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 1390, in patched
    with self.decoration_helper(patched,
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 1372, in decoration_helper
    arg = exit_stack.enter_context(patching)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py", line 526, in enter_context
    result = _enter(cm)
             ^^^^^^^^^^
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 1539, in __enter__
    raise TypeError(
TypeError: autospec creates the mock for you. Can't specify autospec and new.

======================================================================
FAIL: test_initialization (__main__.TestWorkerModule.test_initialization)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 1393, in patched
    return func(*newargs, **newkeywargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/folders/fr/qctyd7d96dn4qrkwypwgr1pr0000gn/T/ipykernel_8439/255302047.py", line 16, in test_initialization
    mockHelper.assert_called_once_with('db')
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 958, in assert_called_once_with
    raise AssertionError(msg)
AssertionError: Expected 'mock' to be called once. Called 0 times.

----------------------------------------------------------------------
Ran 4 tests in 0.003s

FAILED (failures=1, errors=1)
Working on <MagicMock name='os.getcwd()' id='4449251648'>
Working on testing
[25]:
<unittest.main.TestProgram at 0x109289400>

raise an AttributeError:

AttributeError: Mock object has no attribute 'get_path'

Partial class mocking

A class can also be partially mocked by using mock.patch.object:

[26]:
from unittest import TestCase, mock, main
from worker import Worker, Helper


class TestWorkerModule(TestCase):

    def test_partial_patching_decorator(self):
        with mock.patch.object(Helper, 'get_path', return_value='testing'):
            worker = Worker()
            self.assertEqual(worker.helper.path, 'db')
            self.assertEqual(worker.work(), 'testing')

    @mock.patch.object(Helper, 'get_path', return_value='testing')
    def test_partial_patching_decorator(self, mocked_helper_get_path):
        worker = Worker()
        self.assertEqual(worker.helper.path, 'db')
        self.assertEqual(worker.work(), 'testing')
        mocked_helper_get_path.assert_called_once()

main(argv=[''], verbosity=2, exit=False)
test_using_decorator (__main__.TestWorkMockingModule.test_using_decorator) ... ok
test_partial_patching_decorator (__main__.TestWorkerModule.test_partial_patching_decorator) ... ok
test_get_path (__main__.TestWorkerModuleRaisesError.test_get_path) ... ERROR

======================================================================
ERROR: test_get_path (__main__.TestWorkerModuleRaisesError.test_get_path)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 1390, in patched
    with self.decoration_helper(patched,
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 1372, in decoration_helper
    arg = exit_stack.enter_context(patching)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py", line 526, in enter_context
    result = _enter(cm)
             ^^^^^^^^^^
  File "/usr/local/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/mock.py", line 1539, in __enter__
    raise TypeError(
TypeError: autospec creates the mock for you. Can't specify autospec and new.

----------------------------------------------------------------------
Ran 3 tests in 0.005s

FAILED (errors=1)
Working on <MagicMock name='os.getcwd()' id='4449406896'>
Working on testing
[26]:
<unittest.main.TestProgram at 0x1092894c0>

unittest.mock.patch

Patch an import module

unittest.mock is able to patch imports in the module under test using the patch function.

patch will intercept import statements identified by a string and return a Mock instance you can preconfigure using the techniques from unittest.Mock (see above).

Module under test (work.py):

import os

def work_on():
    path = os.getcwd()
    print(f'Working on {path}')
    return path

The project module imports os and uses its getcwd() method , which we want to mock in our test.

Patch using a context manager (with statement)

When using the context manager patch, the patch ends when the with statement ends.

from unittest import TestCase, mock
from work import work_on

class TestWorkMockingModule(TestCase):

    def test_using_context_manager(self):
        with mock.patch('work.os') as mocked_os:
            work_on()
            mocked_os.getcwd.assert_called_once()

We specifically patch the work.os module, not os, as this would patch os for all modules, not just for work.py.

  • The test module imports the project module’s method work_on()

  • At the beginning of the test, the work.os module (which is the os module in our work.py module) is patched with a MagickMock object (here called mocked_os)

  • When the work_on() method is called afterward, that MagicMock object is called instead of the original module (os) - as no return value is defined work_on() returns another MagicMock object - as we are not testing the correctness of the return value here, only checking if the mocked_os executed the getcwd()method, we do not care about the return value

The as statement in the decorator is optional. You may as well not mock the entire work.os module, but rather define a return value for the single function, that is called (getcwd()):

from unittest import TestCase, mock
from work import work_on

class TestWorkMockingModule(TestCase):
    def test_using_return_value(self):
        with mock.patch('work.os.getcwd', return_value='testing'):
            assert work_on() == 'testing'

Patch using the decorator (@patch)

Using the @patch decorator allows to inject a mock into the test function. The patch is available for the entire function.

from unittest import TestCase, mock

from work import work_on

class TestWorkMockingModule(TestCase):

    @mock.patch('work.os')
    def test_using_decorator(self, mocked_os):
        work_on()
        mocked_os.getcwd.assert_called_once()

Here the work.os module is replaced with a MagicMock object.

Patching classes

To test project classes that interact with other project classes in isolation, the other project classes must be mocked (in order to determine test failure to a specific class and method, not by any of its dependency classes).

Example:

Project module (worker.py)

In this module, the Worker class needs to be tested for these two things:

  • the Worker calls Helper with db

  • the Worker returns the expected path supplied by Helper

import os


class Helper:

    def __init__(self, path):
        self.path = path

    def get_path(self):
        base_path = os.getcwd()
        return os.path.join(base_path, self.path)


class Worker:

    def __init__(self):
        self.helper = Helper('db')

    def work(self):
        path = self.helper.get_path()
        print(f'Working on {path}')
        return path

Test module (test_worker.py)

Patch entire class

To test this, the Worker needs to be isolated from Helper, so that failures can be associated with the Worker class only.

Consequently, the entire Helper class must be patched:

from unittest import TestCase, mock
from worker import Worker


class TestWorkerModule(TestCase):

    def test_patching_class(self):
        with mock.patch('worker.Helper') as MockHelper:
            MockHelper.return_value.get_path.return_value = 'testing'
            worker = Worker()
            MockHelper.assert_called_once_with('db')
            self.assertEqual(worker.work(), 'testing')

Note the double return_value in the example, simply using MockHelper.get_path.return_value would not work since in the code we call get_path on an instance, not the class itself.

Alternatively, we can preconfigure the MockHelper instance and assign it as the return value of the worker.Helper call. Below, shown for both the context manager and the decorator way:

from unittest import TestCase, mock
from worker import Worker


class TestWorkerModule(TestCase):

    mockHelperInstance = mock.MagicMock()
    mockHelperInstance.get_path.return_value = 'testing'

    # using patch context manager
    def test_patch_class_context_manager(self):
        with mock.patch('worker.Helper',
                        return_value=self.mockHelperInstance) as MockHelper:
            worker = Worker()
            MockHelper.assert_called_once_with('db')
            self.assertEqual(worker.work(), 'testing')

    # using patch class decorator
    @mock.patch('worker.Helper', return_value=mockHelperInstance)
    def test_patch_class_decorator(self, mock_helper):
        worker = Worker()
        mock_helper.assert_called_once_with('db')
        self.assertEqual(worker.work(), 'testing')

Important

It is important to distinguish between mocking a class and mocking the instance of a class. Sometimes, it is necessary to mock the entire class, sometimes it might be sufficient to mock only an instance from that class.

Mocking the entire class allows for

* checking, in what way that class has been initialized
* instantiating multiple instances from it, which return a Mock object and
  raises the call count

For the above worker.py module, both these tests pass:

from unittest import TestCase, mock
from worker import Worker

mockHelper = mock.MagicMock()


class TestWorkerModule(TestCase):

    def setUp(self):
        mockHelper.reset_mock()
        self.mock_helper_instance = mockHelper.return_value

    @mock.patch('worker.Helper', new=mockHelper)
    def test_initialization(self):
        Worker()
        mockHelper.assert_called_once_with('db')

    @mock.patch('worker.Helper', new=mockHelper)
    def test_get_path(self):
        self.mock_helper_instance.get_path.return_value = 'testing'
        worker = Worker()
        self.assertEqual(worker.work(), 'testing')

Speccing

The danger with mocks is that a Mock object returns a new MagicMock object to basically every attribute or method it is queried for, no matter if that attribute or method actually exists. If the mocked class changes signatures or an attribute, the Mockobject will still return the requested “thing” as another Mock, simply said

The Mock does not know about the object it is mocking.

To prevent this speccing can be added to a Mock, which makes it behave as the object being mocked. The easiest to use is autospec=True option.

Example:

Assuming the Helper class changed its get_path() method to get_folder(), but the call is not changed in the Worker class:

import os


class Helper:

    def __init__(self, path):
        self.path = path

    def get_folder(self):
        base_path = os.getcwd()
        return os.path.join(base_path, self.path)


class Worker:

    def __init__(self):
        self.helper = Helper('db')

    def work(self):
        path = self.helper.get_path()
        print(f'Working on {path}')
        return path

When running the previous test without the speccing option, this would pass, as we mock the get_path method for the Mock instance although the original class does not contain this method anymore.

But with the autospec=True method, the Mockget_path() method, the Mock object checks for consistence with the original object. So

@mock.patch('worker.Helper', new=mockHelper, autospec=True)
def test_get_path(self):
    self.mock_helper_instance.get_path.return_value = 'testing'
    worker = Worker()
    self.assertEqual(worker.work(), 'testing')

raise an AttributeError:

AttributeError: Mock object has no attribute 'get_path'

Partial class mocking

A class can also be partially mocked by using mock.patch.object:

from unittest import TestCase, mock
from worker import Worker, Helper


class TestWorkerModule(TestCase):

    def test_partial_patching_decorator(self):
        with mock.patch.object(Helper, 'get_path', return_value='testing'):
            worker = Worker()
            self.assertEqual(worker.helper.path, 'db')
            self.assertEqual(worker.work(), 'testing')

    @mock.patch.object(Helper, 'get_path', return_value='testing')
    def test_partial_patching_decorator(self, mocked_helper_get_path):
        worker = Worker()
        self.assertEqual(worker.helper.path, 'db')
        self.assertEqual(worker.work(), 'testing')
        mocked_helper_get_path.assert_called_once()

Mocking built-in functions and environment variables

Previously, the print() call in Worker.work() was neglected, though it might be important to test, if that call was made.

Assuming this version of the work() method inside worker.py:

import os

def work_on_env():
    path = os.path.join(os.getcwd(), os.environ['MY_VAR'])
    print(f'Working on {path}')
    return path

The path is concatenated by a MY_VAR environment variable and the os.getcwd() return value. The test:

from unittest import TestCase, mock

from worker import work_on_env

class TestBuiltin(TestCase):

    def test_patch_dict_context_manager(self):
        with mock.patch('worker.print') as mock_print:
            with mock.patch('os.getcwd', return_value='/home/'):
                with mock.patch.dict('os.environ', {'MY_VAR': 'testing'}):
                    self.assertEqual(work_on_env(), '/home/testing')
                    mock_print.assert_called_once_with('Working on /home/testing')
  • we patch the built-in print() function for the worker module as mock_print (patch on import)

  • we patch the worker.work_on_env() method assigning “/home/” as the return value

  • we patch the os.environ dictionary with a {'MY_VAR': 'testing'} dictionary

Nesting multiple context managers can be prevented by using multiple patch decorators:

from unittest import TestCase, mock

from worker import work_on_env

class TestBuiltin(TestCase):
    @mock.patch('os.getcwd', return_value='/home/')
    @mock.patch('worker.print')
    @mock.patch.dict('os.environ', {'MY_VAR': 'testing'})
    def test_patch_builtin_dict_decorators(self, mock_print, mock_getcwd):
        self.assertEqual(work_on_env(), '/home/testing')
        mock_print.assert_called_once_with('Working on /home/testing')

Note that the mock_getcwd must be passed into the test function, though it isn’t used.

— skipped ‘Mocking context managers’ here, might document later —

NEEDS TO BE CONTINUED…