As a Python developer, you may have written non-deterministic code or code that has external dependencies.
One of the simplest ways to test this type of code is mocking.
Mock and Patch — A Brief Recap
Mocking is a powerful technique used in testing to replace real objects with mock objects that simulate the behavior of real objects.
test_file.py
Patching
While Mock objects create stand-alone mock instances, whose interactions and calls can be verfied, patch is a feature that temporarily replaces the real objects in your code with mock objects during the test execution.
This is especially useful for mocking global objects, functions, or classes within modules. Below is an example of patching.
from unittest.mock import patch @patch("src.calculator.Calculator.add"). # patching the add method def test_patch(patched_method_add): patched_method_add.return_value = 10 calculator = Calculator() result = calculator.add(1, 2) assert result == 10
Mocks Couple Your Test To Implementation Detail
Mocks create temporary instances of low-level objects (classes, methods) that can replicate the internal workings of the code under test, within the test itself.
Confusing or Incorrect Patch Targets Lead To False Positives or Brittle Tests
Instead of patching the requests.post
method globally, we should patch the method specifically where it’s used in our PaymentProcessor
class.
from unittest.mock import patch from src.payment_processor import PaymentProcessor @patch("src.payment_processor.requests.post")def test_process_payment(mock_post): mock_post.return_value.json.return_value = {"status": "success"} processor = PaymentProcessor("https://fakegateway.com") result = processor.process_payment(100) assert result is True mock_post.assert_called_once_with("https://fakegateway.com/pay", json={"amount": 100})
What To Do About It?
So you may be asking, OK I get that too much mocking is not good. Where do you draw the line? Should you avoid mocking completely and have your tests use live systems?
Mocking Best Practices
Mock Roles Not Objects
Key points to keep in mind — Mocks are NOT stubs.
When applying “Mock Roles, Not Objects,” you create mocks not to replicate the internal state or the methods of the object, but to verify the interactions between objects based on the roles they play.
Use Fakes Instead of Mocks
So what should you do if you don’t want to use mocks?
Use Fakes instead!
Fakes are ideal when:
-
You want to simulate the behavior of a dependency in a lightweight way.
-
The real dependency is slow, non-deterministic, or difficult to set up (e.g., a database, a web service).
-
You want to avoid the overhead of setting up mocks and verifying interactions.
Test Interfaces Not Implementation
When you test the interface, you’re focusing on what the component is supposed to do rather than how it does it.
Use Autospec To Respect Method Signature
When you mock a method without paying attention to its signature, you risk creating tests that do not accurately reflect how the method is used in real-world scenarios.
To avoid this issue, you can use the autospec
feature provided by the unittest.mock
library.
Where To Use Mocking
All is not doom and gloom when it comes to Mocking.
Mocking has its place and is most useful in the following conditions.
-
Mocking prevents the use of expensive resources — time, compute, money, network calls.
-
Mocking helps create deterministic tests for non deterministic code (e.g. simulate network errors, just raise error by mock client, randomness).
-
Mocking allows us to test objects whose state can’t or shouldn’t be exposed