What is mocking in unit testing?
Updated May 6, 2026
Short answer
Mocking in unit testing is the practice of replacing real dependencies with controlled fake objects that simulate their behavior. It allows developers to test a unit of code in isolation without relying on external systems such as databases, APIs, or file services. Mocks help make tests faster, more predictable, and focused on verifying the behavior of the code being tested.
Deep explanation
In unit testing, the goal is usually to test one small piece of functionality independently. However, real applications often depend on external components that can make tests slow, unreliable, or difficult to control.
Examples of dependencies include:
- Databases.
- External APIs.
- File systems.
- Payment services.
- Email services.
- Third-party libraries.
Mocking solves this problem by replacing these dependencies with fake versions that behave in a predictable way.
For example, instead of testing a user registration function by actually sending an email, a test can use a mock email service that records whether an email would have been sent.
How Mocking Works
A mock object imitates the behavior of a real object but is controlled by the test.
A typical mocking process involves:
- Identify a dependency
- Find an external component that the code relies on.
- Create a mock replacement
- Replace the real dependency with a fake object.
- Define expected behavior
- Configure what the mock should return or how it should respond.
- Run the test
- Execute the code being tested.
- Verify interactions
- Check whether the code used the dependency correctly.
Example:
class PaymentService: def charge(self, amount): # Calls an external payment provider pass
def checkout(payment_service): result = payment_service.charge(100)
if result: return "Order completed"
return "Payment failed"A unit test does not need to call a real payment provider. Instead, it can use a mock:
from unittest.mock import Mock
def test_checkout_success(): mock_payment = Mock() mock_payment.charge.return_value = True
result = checkout(mock_payment)
assert result == "Order completed" mock_payment.charge.assert_called_once_with(100)The test verifies that:
- The checkout logic works correctly.
- The payment service was called with the correct amount.
- No real payment transaction occurred.
Types of Test Doubles
Mocking is one type of test double, which is a general term for objects used in place of real dependencies.
Common types include:
Mock
A mock is a fake object that can simulate behavior and verify interactions.
Example:
email_service.send_email.assert_called_once()This checks whether the code called the email service.
Stub
A stub provides predefined responses but usually does not verify how it was used.
Example:
user_repository.get_user.return_value = { "name": "Alice"}The test controls what data is returned.
Fake
A fake is a simplified working implementation.
Example:
- An in-memory database used instead of a real database.
- A fake file system used during testing.
Spy
A spy records information about calls while allowing some real behavior.
Example:
- Tracking how many times a function was called.
- Recording arguments passed to a method.
Why Use Mocking?
Mocking provides several advantages:
- Isolation: Tests focus on the specific unit being tested.
- Speed: Tests avoid slow external operations.
- Reliability: Tests do not depend on network availability or external systems.
- Control: Developers can simulate different situations, including failures.
- Better failure testing: Teams can test scenarios such as API errors or unavailable services.
For example, testing an application when a payment API fails is much easier with a mock:
mock_payment.charge.return_value = FalseThe test can verify that the application handles payment failure correctly.
Trade-offs of Mocking
Although mocking is useful, overusing it can create problems.
Potential issues include:
- Tests may become tightly coupled to implementation details.
- Excessive mocking can make tests difficult to maintain.
- Mocks may behave differently from real dependencies.
- Tests may pass even when the real system has problems.
A good rule is to mock external boundaries, such as APIs and databases, while avoiding unnecessary mocking of simple internal logic.
Real-world example
Imagine an online shopping application where placing an order involves charging a customer's credit card.
The production code might look like:
class OrderService: def __init__(self, payment_gateway): self.payment_gateway = payment_gateway
def place_order(self, amount): success = self.payment_gateway.charge(amount)
if success: return "Order placed"
return "Order failed"A unit test should not charge a real credit card. Instead, it uses a mock payment gateway:
from unittest.mock import Mock
def test_order_success(): payment_gateway = Mock() payment_gateway.charge.return_value = True
service = OrderService(payment_gateway)
result = service.place_order(50)
assert result == "Order placed" payment_gateway.charge.assert_called_once_with(50)The mock allows the developer to test the order logic without:
- Connecting to a payment provider.
- Making real transactions.
- Waiting for network responses.
- Handling external service failures.
The test only checks whether the order service behaves correctly based on the payment result.
Common mistakes
- * Mocking every dependency, including simple internal objects that do not need replacement.
- * Using mocks to hide poorly designed code instead of improving the design.
- * Assuming a mocked dependency behaves exactly like the real system.
- * Verifying too many internal method calls instead of testing expected behavior.
- * Forgetting to test failure scenarios using mock responses.
- * Creating mocks that are more complex than the real dependency.
- * Using mocks without understanding what behavior the real dependency provides.
- * Allowing tests to pass with unrealistic mock configurations.
Follow-up questions
- Why are mocks useful in unit testing?
- What is the difference between a mock and a stub?
- When should you avoid using mocks?
- What is the difference between mocking and dependency injection?
- Can mocking replace integration testing?
- What should you verify when using mocks?