Latticework

Command Palette

Search for a command to run...

Testing

Mocking

18 min

Explanation

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)
Try it

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.

Loading editor…
Explanation

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.

Exercise

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`.

Exercise

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>}`.

Quiz

Why use a 'fake' or 'mock' object instead of a real dependency (like a real payment gateway) when writing a unit test?

Checkpoint

You can write a fake/mock object that stands in for an external dependency, letting you test code's behavior without real side effects.