Mocking and Stubbing in Tests, Explained

Mocking and stubbing are often used interchangeably, but they’re genuinely distinct testing techniques serving different purposes — worth knowing the actual difference.

from unittest.mock import Mock

# STUB: provides canned, predetermined responses - you don't care HOW it's called,
# just that it returns the right data for the test to proceed
def get_weather_stub(city):
    return {"temp": 22, "condition": "sunny"}  # always the same, regardless of input

# MOCK: additionally verifies HOW it was called - genuinely useful for
# testing that a specific interaction actually happened
payment_mock = Mock()
process_order(order, payment_service=payment_mock)

payment_mock.charge.assert_called_once_with(amount=59.99, currency="EUR")
# This test FAILS if charge() was never called, or called with the wrong arguments -
# a stub alone couldn't verify this interaction happened at all

When to Use Which

  • Stubs — when you just need a dependency to return specific data so the test can proceed (e.g., faking a database query result)
  • Mocks — when the actual behavior under test IS the interaction itself (e.g., verifying a payment was genuinely charged, an email was genuinely sent)

The Overuse Trap

Mocking every single dependency, including simple, cheap-to-use real objects, produces tests that verify implementation details rather than genuine behavior — brittle tests that break on any internal refactor, even when the actual observable behavior hasn’t changed at all. Mock genuinely external, slow, or side-effecting dependencies (network calls, payment APIs); let simple, fast internal logic run for real wherever practical.