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)
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
__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
__exit__ methods of a context manager are the most important. The
__enter__ method is called whenever a context manager is invoked on a class.
__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
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
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
- 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 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)
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.
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.
Here is the complete syntax and arguments of the function.
unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)
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
AsyncMock show their flexibility and versatility when it comes to creating mocks.