Everything About Python Mock Context Manager

In this article, we will be discussing the concept of Python Mocking and Context Managers and how we are able to use them in our programs for testing.

What is Mocking?

Mocking is the usage of a mock object in order to imitate an actual object in a program. Mocking is a vital tool in order to improve program testing. An example of using Mocks is on programs that give requests to APIs.

Changing certain things within the code while testing its behavior in different conditions can cause errors. Thereby leading to the whole program collapsing. It’s better to simulate unfavorable conditions using a “Mock Object.”

About the Module

Mock Objects can be created using the unittest.mock library. As of Python 3.10, It is part of the Python standard library and comes under the Development Tools category.

The most important class, Mock, allows us to imitate objects within our program with Mock Objects. Try out the following commands using our Online Interpreter.

Importing The Module

from unittest.mock import Mock

Creating a Basic Mock Object

The primary class of this module is Mock. There are unlimited use cases for this class due to its versatility. Let’s look at how we can initialize a mock object.

from unittest.mock import Mock
myMock = Mock()
print(myMock)

Output

Basic Mock Object

What is a Context Manager in Python

Resources can be allocated and released when needed using Python context managers. A standard context manager is defined using the with statement. If you want to run two related tasks as a pair, with some commands between them, take the following example.

with open('content/myfile.txt', 'w') as myFile:
    myFile.write('Welcome to PythonPool!')

The above program opens the file myfile.txt and proceeds to write in some text. In this case, after editing the file, there was no need to run close(). Python context managers automatically perform the close operation and free up memory

Creating a Mock Asynchronous Context Manager in Python

With the help of the modules AsyncMock and MagicMock, we are able to create Asynchronous Context Managers. An asynchronous context manager allows us to execute within __aenter__ and __aexit__.

__aenter__ and __aexit__ are the provided instances that return the async function.

The syntax for creating an asynchronous context manager is async with. Let’s look at the following Asynchronous context manager class.

class AsyncContextManager: 
    async def __aenter__(self): 
        await log('Within the Context Manager') 
 
    async def __aexit__(self, exc_type, exc, tb): 
        await log('Outside the Context Manager')

Python Mock Context Manager __enter__ and __exit__

The __enter__ and __exit__ methods of a context manager are the most important. The __enter__ method is called whenever a context manager is invoked on a class.

The __exit__ method is executed regardless of the result of the __enter__ block. Let’s refer to the following example:

with myContext(foo) as bar:
    do_stuff(bar)

contextObject = myContext(foo)
bar = contextObject.__enter__()
try:
    do_stuff(bar)
finally:
    contextObject.__exit__(None)

Python Mock Context Manager patch()

The standard module unittest.mock() provides a tool for mocking program objects called patch(). It searches for the object within a program and automatically replaces it with an Mock object.

The patch() is usually used as a decorator or as a context manager.

patch() as a Context Manager

For the following cases, a context manager is used:

  • Only objects within the test scope should be replaced with a mock object.
  • Usage of multiple decorators affects the test result’s readability.

In order to use patch() as a context manager, the with statement is used.

import unittest
from requests.exceptions import Timeout
from unittest.mock import patch

class TestClass(unittest.TestCase):
    def testFunction(self):
        with patch('mySample.requests') as mock_requests:
            mock_requests.get.side_effect = Timeout
            with self.assertRaises(Timeout):
                getRequests()
                mock_requests.get.assert_called_once()

if __name__ == '__main__':
    unittest.main()

patch() as a Decorator

You can use patch() as a function decorator if you wish to mock an object throughout your entire test function. Let’s look at the following examples

import unittest
from requests.exceptions import Timeout
from unittest.mock import patch

class TestClass(unittest.TestCase):
    @patch('mySample.requests')
    def testFunction(self, mock_requests):
            mock_requests.get.side_effect = Timeout
            with self.assertRaises(Timeout):
                getRequests
                mock_requests.get.assert_called_once()

if __name__ == '__main__':
    unittest.main()

Implementing Multiple Context Managers using a List of Context Managers in Python 3.3

From Python 3.3, it is possible to enter an n-length list of context managers using the class contextlib.ExitStack. contextlib.ExitStack is a context manager that allows us to combine multiple other context managers and clean up functions.

Here’s an example:

with ExitStack() as stack:
    for eachManager in contextManagers:
        stack.enter_context(eachManager)

FAQs

Why do we care about mocking context managers?

Mocking context managers allow us to replicate the properties of default context managers. A context manager allocates resources for tasks like establishing a connection and initiating a clean process once it’s over. These steps can be replicated using a mock context manager.

Why do we need mocks to write tests?

Changing certain things within the code while testing can cause problems. Therefore replacing particular objects with mocks allows us to create test scenarios without affecting the actual program.

What are the arguments in unittest.mock.patch?

Here is the complete syntax and arguments of the function.

unittest.mock.patch(targetnew=DEFAULTspec=Nonecreate=Falsespec_set=Noneautospec=Nonenew_callable=None**kwargs)

Conclusion

We have looked at the concept of Mocking and Context managers. We have also looked at how those two concepts come together and allow easy testing of certain or all objects within our program base. Modules like unittest and AsyncMock show their flexibility and versatility when it comes to creating mocks.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments