1. why we use patch
Isolation:
When testing a function, you want to focus on the logic of that function without interference from other parts of the system. By patching dependencies, you ensure that your tests are not affected by other components’ behavior.
Avoid External Dependencies:
If a function relies on external resources (e.g., a database, a web service), patching allows you to simulate these dependencies without making actual calls. This makes tests faster and more reliable.
Control Over Function Behavior:
You can define the return value of a patched function to test different scenarios, including edge cases and error conditions, which might be hard to reproduce with the actual function.
Ensuring Correct Interactions:
Patching allows you to verify that a function interacts with its dependencies in the expected way. You can check that certain methods are called with specific arguments.
2 example
assume you have this module structure:
project/
├── math_operations.py
└── test_math_operations.py
here are the code for these two file
# math_operations.py
def external_service(a, b):
# Imagine this function makes a network call to an external service
return a + b
def add(a, b):
return external_service(a, b)
# test_math_operations.py
import unittest
from unittest.mock import patch
from math_operations import add
class TestMathOperations(unittest.TestCase):
@patch('math_operations.external_service')
def test_add(self, mock_external_service):
# Arrange
mock_external_service.return_value = 10
# Act
result = add(3, 7)
# Assert
mock_external_service.assert_called_once_with(3, 7)
self.assertEqual(result, 10)
if __name__ == '__main__':
unittest.main()
how to run
python -m unittest test_math_operations.py
3 explanation
- mock_external_service is the mock object that replaces the external_service function in the math_operations module.
- This mock object allows you to define custom behavior for external_service during the test.
- mock_external_service.return_value = 10 specifies that whenever external_service is called, it should return 10.
- mock_external_service.assert_called_once_with(3, 7) checks that external_service was called exactly once with the arguments 3 and 7.
- If external_service was not called, or was called with different arguments, or was called more than once, this assertion will fail, causing the test to fail.
note: for other method like assert_called_once_with, you can check unittest.mock.py to get more.
4 Parameters of patch
patch(
target, new=DEFAULT, spec=None, create=False,
spec_set=None, autospec=None, new_callable=None, *, unsafe=False, **kwargs
)
target:
This is the dotted path to the object you want to patch. It should be a string in the format ‘module.ClassName’ or ‘module.function_name’. For example, ‘math_operations.external_service’.
new (default: DEFAULT):
This specifies the new object that will replace the target during the patch. If not specified, a MagicMock will be used as the default replacement.
spec (default: None):
This allows you to specify a class or instance that the mock should emulate. The mock will only allow attributes that exist on the specified class or instance, which helps to prevent errors due to incorrect attribute usage.
create (default: False):
If True, patch will create the attribute if it does not exist. This is useful for dynamically adding attributes to objects.
spec_set (default: None):
Similar to spec, but stricter. Once the attributes are set, they cannot be changed, which helps in locking down the mock to a specific set of attributes.
autospec (default: None):
If True, patch will use the function or class signature to create a more accurate mock. This ensures that the mock has the same signature as the original object, making it more realistic and preventing mistakes.
new_callable (default: None):
This is a callable that returns the new object to be used as the mock. If provided, this will override the default MagicMock.
unsafe (default: False):
If True, this disables safety checks that are normally performed. For instance, if you need to mock an attribute that normally cannot be modified, setting unsafe=True would allow you to bypass this restriction. Use with caution as it can lead to hard-to-debug issues.
**kwargs:
Additional keyword arguments that are passed to the mock object constructor. This allows for more customization of the mock object.