Course contentsShow
← C#
Lesson 1630 of 1,90815. Software Quality and TestingFree lesson

Distinguishing Mocks, Stubs, and Fakes

Select the appropriate test double type based on whether the goal is interaction or state verification.

In this lesson, you will learn how to choose the correct test double to isolate your unit tests, ensuring you are testing the right thing at the right time.

When unit testing, we often replace real dependencies with "test doubles." Choosing the wrong one leads to brittle tests that break during refactoring or fail to detect actual regressions. We distinguish them based on how they facilitate your test.

The Three Categories

1. Stubs: For State Verification

A Stub is a minimal implementation that provides indirect input to your code under test. You use a stub when you don't care how your code calls a dependency, only that the dependency provides the data necessary for the code to perform its job.

  • Goal: Feed the method.
  • Example: A stubbed IUserRepository that always returns a hardcoded User object so you can test your DiscountCalculator logic.

2. Mocks: For Interaction Verification

A Mock is an object programmed with expectations. You use a mock when the behavior—the specific interaction—is the primary requirement. If your code must call an EmailService exactly once with specific arguments, you use a mock.

  • Goal: Verify the contract/protocol.
  • Example: Using Moq to verify that _emailService.Verify(x => x.Send("admin@test.com"), Times.Once()).

3. Fakes: For Functional Realism

A Fake is a working implementation, but one that is simplified and unsuitable for production. An in-memory database or a file system wrapper that stores data in a Dictionary are classic fakes.

  • Goal: Represent a simplified operational dependency.
  • Example: A FakeDatabase class that stores objects in a List<T> instead of calling SQL, allowing for faster integration testing without external overhead.

Choosing the Right Tool

The decision hinges on your testing goal:

  • If you care about the output (State): Use a Stub or a Fake. You are validating the "what."
  • If you care about the process (Behavior): Use a Mock. You are validating the "how."

Over-mocking creates tests that are tightly coupled to the implementation details of your code. If you find yourself mocking every single internal method call, you are likely testing the implementation rather than the observable behavior. Prefer Stubs for data-heavy tests and reserve Mocks for critical interactions where the side effect (like sending an email or charging a card) is the primary outcome.

Key Takeaway: Use Stubs/Fakes to provide necessary data for state verification, and use Mocks only when you must strictly enforce and verify the internal behavioral protocol of your dependencies.