Logo
Logo
Home
Archive
AI Agent Notes
Advertise
YouTube
Login
Sign Up
Logo
  • Home
  • Posts
  • 🦥 Unit Testing: Test Doubles

🦥 Unit Testing: Test Doubles

Feb 18, 2025

Hello friends!

Welcome to this week’s Sloth Bytes!

I hope you had a horrible week. 😉

🦥 No selling out today

I am genuinely considering selling feet pics, so if you work at a company with a marketing budget please forward this to your boss immediately or the feet come out.

Save yourself and learn more about sponsoring

Sloths prefer to eat leaves from certain trees.

Giphy

A sloth will rotate among approximately 7 to 12 favorite feeding trees.

It’s a a strategy that prevents them from overeating any one kind of leaf.

Unit Testing: Test Doubles

I’ve recently been studying some programming concepts…

(wow programming YouTuber needs to study programming)

I was specifically researching about unit tests because I don’t test my code enough and never really learned how.

I came across these interesting terms for unit testing:

Mocking, Stubbing, and Spying.

What’s interesting about these concepts is they’re all strategies for what’s called:

Test Doubles.

What the flip is a Test Double?

“Test double” is an umbrella term for a replacement you use in a test instead of a real collaborator—kind of like a stunt double. Different doubles answer different questions: “what value should this dependency return?”, “was this method called?”, or “can I use a lightweight working implementation instead?”

Why do we need Test Doubles?

Test doubles are useful when the code under test collaborates with something slow, expensive, nondeterministic, unavailable, or difficult to force into a specific failure mode.

We have to test our code on:

  • Third-party APIs

  • Payment/email providers

  • Databases or queues

  • Clocks, randomness, and external files

  • Rare failures such as timeouts and rejected requests

Replacing one of those collaborators can make a unit test fast and deterministic. The tradeoff is important: the more fake behavior you invent, the more likely your test passes while the real integration is broken.

Other benefits are

  • Faster tests

  • Simplified tests

  • More control

Types of Test Doubles

Terminology varies a little between testing communities, but these are common categories:

1. Mocks

  • A mock is commonly used to verify interactions: which collaborator method was called, with what arguments, and sometimes how many times.

  • Mocking frameworks often let you configure return values too, so in real code the words “mock” and “stub” sometimes overlap.

  • The important distinction is the test’s intent: are you checking the result, or checking how your code interacted with a dependency?

test('saves the new user', async () => {
  const database = {
    save: jest.fn().mockResolvedValue({ id: 123 })
  };

  const service = new UserService(database);
  await service.createUser(userData);

  expect(database.save).toHaveBeenCalledTimes(1);
  expect(database.save).toHaveBeenCalledWith(userData);
});

2. Stubs

  • A stub provides controlled answers so you can drive the code under test down a particular path.

  • Examples: return a known exchange rate, make an API return 503, or make a clock return a fixed timestamp.

  • The assertion usually focuses on the behavior/output of your code, not whether the stub itself was called in a particular way.

A stub is useful when the dependency’s response is just an input to the scenario you are testing.

const paymentStub = {
  charge: async () => ({
    status: 'declined',
    reason: 'insufficient_funds'
  })
};

const service = new CheckoutService(paymentStub);
const result = await service.checkout(order);

expect(result.status).toBe('payment_failed');

3. Spies

  • A spy records calls so a test can inspect interactions later.

  • Some frameworks spy on a real method by default; others let you replace the implementation while keeping the call tracking.

  • Be careful when spying on real I/O. A test that calls the real database/payment/email service is no longer isolated just because a spy is watching it.

test('emits a welcome notification', async () => {
  const notifier = {
    sendWelcome: jest.fn().mockResolvedValue(undefined)
  };

  const service = new UserService(notifier);
  await service.createUser(userData);

  expect(notifier.sendWelcome).toHaveBeenCalledWith(userData.email);
});

Two more doubles worth knowing

Fake: a lightweight working implementation, such as an in-memory repository that implements the same interface as your database repository. It has real behavior, just not production infrastructure.

Dummy: a value required only to satisfy a parameter/interface but never actually used by the behavior under test.

Don’t mock the universe

Test doubles are useful at boundaries, but too much mocking can couple tests to implementation details. Then you refactor perfectly correct code and 47 tests explode because they expected three private method calls in a sacred order.

  • Prefer asserting observable behavior when possible.

  • Mock external boundaries and expensive/nondeterministic collaborators—not every internal function.

  • Keep integration or contract tests for important APIs, databases, queues, and serializers so your fake assumptions are checked against reality.

  • Use doubles to make failure paths easy to reproduce: timeout, retryable 503, duplicate event, permission failure, malformed response, etc.

If you want to keep learning

  • Debugging techniques — isolate the bug first, then write a test that proves you actually fixed it.

  • Error handling explained — test doubles are especially useful for simulating failures from databases, files, and external services.

  • APIs explained — understand the external dependencies you’ll often mock or stub in application tests.

xAI releases its newest model Grok 3 (5 minute read)

xAI, released its latest flagship AI model Grok 3 along with new capabilities in the Grok app for iOS and the web.

Create React App is now deprecated (9 minute read)

The library for web and native user interfaces

Reddit CEO Says Paywalls Are Coming Soon (2 minute read)

Some subreddits will require you to pay to see content.

Debugging An Undebuggable App (24 minute read)

This app has a surprising number of anti-debugging protections. Let's figure out how to bypass them.

Replace your JavaScript Animation Library with View Transitions (15 minute read)

Use the View Transitions API to create smooth animations between DOM states with minimal CSS and JavaScript, replacing heavy animation libraries.

Thank you to everyone who submitted 😃 

RelyingEarth87, porrrq, E-Sieben, ravener, in1yan, Raufirzaman, paarthjuneja, GabrielDornelas, and tobiaoy.

Let’s have an easier week.

Phone Number Formatting

Create a function that takes a list of 10 numbers (between 0 and 9) and returns a string of those numbers formatted as a phone number (e.g. (555) 555-5555).

Examples

format_phone_number([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])
output = "(123) 456-7890"

format_phone_number([5, 1, 9, 5, 5, 5, 4, 4, 6, 8])
output = "(519) 555-4468"

format_phone_number([3, 4, 5, 5, 0, 1, 2, 5, 2, 7])
output = "(345) 501-2527"

Notes

Don't forget the space after the closing parenthesis.

How To Submit Answers

Reply with

  • A link to your solution (github, twitter, personal blog, portfolio, replit, etc)

  • or if you’re on the web version leave a comment!

  • If you want to be mentioned here, I’d prefer if you sent a GitHub link or Replit!

2 Videos might come out this week

Yeah you heard me, double upload, but uh that’s because it’s almost been a month since the last upload (whoops.)

That’s all from me!

Have a great week, be safe, make good choices, and have fun coding.

If I made a mistake or you have any questions, feel free to comment below or reply to the email!

See you all next week.

Keep Reading

Read all
arrow-right
envelope-simple

Join 50k+ developers and become a better programmer and stay up to date in just 5 minutes.

© 2026 Sloth Bytes.
beehiivPowered by beehiiv