Stop 12 · TestingTopics · 6/9

Mocking & Stubbing

Part of the Backend Developer Roadmap

In short

Your signup code sends an email and charges a card. You don't want tests spamming inboxes or charging real money. Mocks and stubs swap those parts for fakes you control.

Replacing real dependencies (a database call, an external API, the current time) with fake, controllable versions in your tests — so tests are fast, deterministic and isolated.

The blob character in pads and helmet bats against a bowling machine in the nets.

Think of it like this

The bowling machine in the nets

In the nets, a bowling machine sends down exactly the delivery you set, every single time. No tired bowler, no surprises. A stub is that machine: a fake dependency that returns exactly what the test needs. A mock goes one step further and also records every ball it bowled.

Watch it happen

Testing signup with a fake mailer

Test
signup()
Fake mailer

stub: resolve ok

1/5The test replaces the real email client with a fake that always succeeds.

The big ideas

What you need to know

Stub: canned answers

A stub returns fixed values, like 'payment succeeded', so you can test what your code does next.

Mock: records calls

A mock or spy also remembers how it was called, so you can assert 'sent exactly one email'.

Inject dependencies

Pass the mailer or payment client into your function. Then swapping in a fake is trivial.

Don't mock everything

Mock what you don't own, like email and payment APIs. Mocking your own queries can hide real bugs.

See it in code

Walk through the code

ts
1import { jest, test, expect } from "@jest/globals";2import { signup } from "./signup";3 4test("sends a welcome email after signup", async () => {5  const mailer = { send: jest.fn(async () => ({ ok: true })) };6  const users = { create: jest.fn(async (email: string) => ({ id: 1, email })) };7 8  await signup("riya@test.dev", { mailer, users });9 10  expect(users.create).toHaveBeenCalledWith("riya@test.dev");11  expect(mailer.send).toHaveBeenCalledTimes(1);12  expect(mailer.send).toHaveBeenCalledWith(expect.objectContaining({ to: "riya@test.dev" }));13});

1/3Fakes for the mailer and user store. jest.fn records every call and returns what you tell it to.

This works because signup takes its dependencies as an argument. For imports you can't inject, jest.mock() can replace a whole module.

Save yourself the pain

Common mistakes

Oops

Mocking so much that the test only checks your mocks.

Do this instead

If every collaborator is fake, the test proves little. Mock the edges and keep your real logic real.

Oops

Leaving mocks in place between tests.

Do this instead

Call jest.restoreAllMocks() or turn on restoreMocks in config, so one test's fakes don't leak into the next.

Your mission

Fake a payment gateway

~30 min

Quick check

You want your test to check that sendEmail was called exactly once. What do you need?

Go deeper

  1. 1Mock a third-party API call in a test so it doesn't make a real network request.
  2. 2Stub a database call to return known test data without hitting a real database.
  3. 3Learn the difference between a mock (verifies it was called) and a stub (just returns data).

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge