Mocking
18 min
Testing code that talks to something external — a database, a payment processor, an email service — is risky if the test uses the REAL thing: slow (network calls), unreliable (the external service might be down), and dangerous (you really don't want a test suite sending real emails or charging real credit cards every time it runs).
A fake (or mock) replaces the real dependency with a simple stand-in that mimics its interface without any real side effects:
class FakeEmailSender:
def __init__(self):
self.sent = []
def send(self, to, subject):
self.sent.append((to, subject))
return len(self.sent)
notify_user doesn't know or care that it's talking to a fake -- it just calls sender.send(...), exactly as it would with a real email service. That's dependency injection: pass the dependency in, don't hardcode which implementation you're using.
A fake also gives your test something to ASSERT against — after running
code that should have sent an email, checking fake.sent confirms it
actually happened, without needing a real inbox to check:
class FakePaymentGateway:
def __init__(self):
self.charges = []
def charge(self, amount):
receipt_id = len(self.charges) + 1
self.charges.append(amount)
return receipt_id
Real testing frameworks (like unittest.mock in Python's standard
library) provide tools to generate fakes automatically and make
assertions about how they were called — but understanding the concept by
hand-writing one, like here, makes it obvious what those tools are
actually doing under the hood.
Given `FakeEmailSender` and a module-level `emailer` instance below, implement `send(self, to, subject)`: append `(to, subject)` to `self.sent`, and return the new length of `self.sent`.
Given `FakePaymentGateway` below, write `process_order(amount, gateway)`: if `amount <= 0`, return `{'success': False, 'receipt_id': None}`. Otherwise call `gateway.charge(amount)` and return `{'success': True, 'receipt_id': <that result>}`.
Why use a 'fake' or 'mock' object instead of a real dependency (like a real payment gateway) when writing a unit test?
You can write a fake/mock object that stands in for an external dependency, letting you test code's behavior without real side effects.