📣 Requestly API Client – Free Forever & Open Source. A powerful alternative to Postman. Try now ->

Mock vs Stub vs Fake: Core differences

Rohit Rajpal
This guide explains the differences between mocks, stubs, and fakes in API testing and helps you choose the right one for your test case.

APIs, databases, and third-party services often introduce instability into tests. They can be slow, unavailable, or behave inconsistently across environments. These issues make tests unreliable and delay feedback during development.

Test doubles such as mocks, stubs, and fakes help solve this by simulating external dependencies in controlled ways. They allow tests to focus on application logic without depending on actual systems. Each type serves a different purpose, and choosing the right one depends on what behavior you are testing and what outcome you want to validate.

This guide explains what mocks, stubs, and fakes are, how they differ, when to use them, and how to apply them effectively in your testing workflow.

What Are Mocks, Stubs, and Fakes?

Mocks, stubs, and fakes are categories of test doubles that replace real components in a test environment. They help isolate the unit under test by simulating the behavior of external dependencies.

What is a Stub

A stub is a controllable replacement for a dependency that provides fixed responses to method calls. It does not contain logic or behavior beyond what is explicitly defined.

  1. Purpose: Control the return value or output of a function or service call.
  2. Behavior: Passive; only responds as configured and does not record how it is used.
  3. Example: A pricing service stub that always returns ₹100, regardless of the product.

What is a Mock

A mock replaces a real object and tracks how it is interacted with. It is primarily used to verify behavior whether methods were called, how many times, and with what arguments.

  1. Purpose: Validate that interactions happened as expected.
  2. Behavior: Active; includes built-in mechanisms to record and assert calls.
  3. Example: A mock email service that records whether sendWelcomeEmail(userId) was called after signup.

What is a Fake

A fake is a working but simplified version of a real system component. It usually implements real logic but does so with test-safe constraints or in-memory data.

  1. Purpose: Provide functional logic for integration without external dependencies.
  2. Behavior: Executes real operations using fake or local infrastructure.
  3. Example: An in-memory repository that mimics a database’s save and retrieve methods.

Why Use Mocks, Stubs, and Fakes in Testing?

Testing against real systems often introduces unnecessary complexity. External services can be slow, unreliable, or incomplete during early development. Test doubles, specifically mocks, stubs, and fakes, allow you to replace these systems with controlled alternatives that behave in ways you define.

Using test doubles leads to tests that are:

  • Faster to run, since they don’t depend on real network or infrastructure
  • More stable, because they don’t break due to third-party failures
  • Easier to maintain, as they remove complicated environment setup
  • Better scoped, so each test targets specific logic without unintended side effects

While the overall goal is improved speed, reliability, and control, the exact benefit depends on which type of double you use.

Benefits of Using Mocks

Mocks allow you to track and verify whether specific methods were called, how many times they were called, and with what arguments.

Key advantages:

  1. Verifies behavior precisely: Mocks let you confirm that certain side effects or calls happened. For example, you can check if a notification service was triggered only after a valid transaction completes.
  2. Supports conditional workflows: You can assert that a fallback method was used after a failure or that retries happened according to policy. This is useful for testing resilience mechanisms.
  3. Prevents false confidence in test outcomes: Instead of assuming a service worked, mocks let you prove that it was called with the right data. This is critical for systems where downstream actions matter.
  4. Improves test focus: Mocks help isolate behavioral expectations. Instead of checking outputs, you define and validate interactions, which makes the intent of the test clear.
  5. Simplifies assertion logic: Most mocking libraries support detailed verification syntax that makes behavior testing concise and expressive.

Use mocks when your concern is how your code interacts with other services, not what those services return.

Benefits of Using Stubs

Stubs are designed to simplify control over inputs and outputs. They allow you to simulate a function or service’s response so you can test how your code behaves in different conditions without triggering the real logic.

Key advantages:

  1. Enables targeted logic testing: You can simulate specific inputs or errors to force a particular branch of logic to execute, such as simulating an expired token or a failed lookup.
  2. Removes dependency unpredictability: When real APIs fluctuate due to data changes or rate limits, stubs provide consistent and reproducible outputs.
  3. Reduces complexity in setup: You don’t need to configure a real system with certain states. Instead, a stub can return exactly what the test needs, without setup or cleanup.
  4. Accelerates test development: When upstream services are unavailable or under construction, stubs allow downstream teams to write tests and implement logic in parallel.
  5. Facilitates scenario coverage: Edge cases like slow responses, malformed data, or missing fields can be hard to reproduce in real systems. Stubs give you full control over those scenarios.

Use stubs when you care about how your code responds to specific values, not about verifying whether the upstream service was called or used correctly.

Benefits of Using Fakes

Fakes offer a working implementation of a dependency that behaves like the real thing but uses a simplified or in-memory version. They are useful when you need to run logic against functional components without relying on external infrastructure.

Key advantages:

  1. Supports realistic integration behavior: Fakes often simulate entire subsystems, such as in-memory databases, queues, or cache layers. This lets you test flow without full deployment.
  2. Useful for multi-step tests: If a feature requires saving and retrieving data, fakes allow you to run both steps without relying on an actual backend or datastore.
  3. Speeds up testing while preserving behavior: Since fakes use the same interface as the real system, you get more accurate results than with stubs, but without the overhead of external systems.
  4. Encourages early integration checks: Even if real infrastructure is unavailable, fakes allow developers to simulate data persistence, message delivery, or authorization logic in test environments.
  5. Improves performance in broader test suites: For integration or system-level tests, replacing databases or services with fakes avoids slowness while still verifying behavior through full workflows.

Use fakes when your test requires real behavior but not real infrastructure. They are especially useful when the component under test interacts with multiple layers that should behave realistically.

Key Differences Between Mocks, Stubs, and Fakes

Mocks, stubs, and fakes are all forms of test doubles, but they serve different testing goals and work in fundamentally different ways.

Here is a table that explains the differences between API mocks, stubs, and fakes.

AspectStubMockFake
Primary purposeControl return valuesVerify that interactions occurredProvide working but simplified implementation
Focus of testOutcome-based logicInteraction and behaviorEnd-to-end logic using test-safe versions
Implements real logicNoNoYes
Tracks method callsNoYesSometimes, for debugging
Verification supportNot supportedBuilt-in (assertions, call tracking)Indirect, through behavior or side effects
Complexity levelVery lowMedium (due to configuration and verification)Medium to high (real code, needs to be maintained)
Execution path coverageSingle-path or branch-specific testingTriggered when testing alternate flows, retries, fallbacksSupports realistic flow across multiple components
Dependency simulation styleStatic responseDynamic observationReal behavior without production overhead
Test scope suitabilityUnit testsUnit or contract-style interaction testsIntegration, end-to-end, or acceptance tests
Best for testingDecision branches, error handlingNotification triggers, logging behavior, interaction countData flow, persistence, business logic flow
Risk if misusedMasked logic bugs due to oversimplified responsesBrittle tests that fail on non-essential interaction changesOverconfidence in test coverage if fake diverges from real impl.
Ideal development stageEarly logic developmentMid-stage behavior testingLater-stage system or workflow validation

Examples of Using Mocks, Stubs, and Fakes

Consider a backend API being developed to handle user registration. When a new user signs up, the system performs three tasks:

  1. Validates input and stores user data
  2. Sends a verification email
  3. Logs the registration event to an external analytics service

The external email and analytics services are not always available or reliable during development or testing. Here’s how mocks, stubs, and fakes can help in this situation.

1. Using Mocks

Imagine testing whether the registration service properly logs the event to the analytics platform. A mock analytics client can be injected into the registration flow. After triggering registration, the test verifies whether the trackEvent() or similar method was called with the expected payload.

const analyticsMock = { trackEvent: jest.fn() };
registerUser(data, analyticsMock);
expect(analyticsMock.trackEvent).toHaveBeenCalledWith({
event: 'UserRegistered',
userId: data.id,});

2. Using Stubs

The next step involves verifying the user’s email through an external service. The system’s behavior changes depending on whether the verification succeeds or fails. By injecting a stubbed version of the email verification service, the test can simulate specific outcomes and confirm that the registration logic handles them appropriately.

const emailServiceStub = {
verifyEmail: jest.fn(() => ({ status: 'verified' })),};
const result = registerUser(data, { emailService: emailServiceStub });
expect(result.status).toBe('success');

3. Using Fakes

Once the registration flow reaches the point where user data must be stored, tests should validate persistence. If the real database is too slow or unavailable, an in-memory fake repository can be used to ensure the system saves and retrieves users correctly without external dependencies.

class FakeUserRepo {
constructor() { this.users = [];
}
save(user) { this.users.push(user);
return user;
}
findByEmail(email) { return this.users.find(u => u.email === email);
}
}
const repo = new FakeUserRepo();
registerUser(data, { userRepo: repo });
expect(repo.findByEmail(data.email)).toBeDefined();

Dos and Don’ts When Using Test Doubles

Test doubles can improve test coverage, speed up execution, and isolate failures. But they introduce trade-offs that must be carefully managed. The tables below outline specific dos and don’ts for mocks, stubs, and fakes.

Dos and Don’ts When Using Mocks

Mocks are designed to verify whether specific interactions happened. They are best suited for behavior verification, especially in cases where the system under test triggers side effects like event logging, notifications, or callbacks. However, careless use can make tests brittle and overly focused on implementation details.

DoDon’t
Use mocks to verify external interactions like logging or event emissionsUse mocks to simulate return values—use stubs instead
Keep interaction expectations specific and meaningfulOververify every method call or parameter unnecessarily
Isolate mocks to the unit being testedLet mocks leak into unrelated parts of the test or setup
Name mocks clearly based on role (e.g., eventBusMock)Use vague names like mock1 or serviceMock
Fail tests only on contract-breaking behaviorAssert implementation details that are likely to change

Dos and Don’ts When Using Stubs

Stubs provide controlled outputs to test how a system behaves under specific conditions. They are not meant to validate interactions or real implementations. Misusing stubs often leads to confusing tests that neither isolate dependencies nor clearly communicate the intent.

DoDon’t
Use stubs to simulate external systems returning fixed responsesUse stubs to track how many times a method was called
Keep return values simple and deterministicAdd conditional logic or branching to stubs
Use stubs to trigger success/failure pathsMix stubs and mocks in the same test unless necessary
Document why the stub is used and what it controlsLet stubs silently affect unrelated parts of the system
Stub interfaces or contracts, not internal utility methodsStub deep internals or static methods that break isolation

Dos and Don’ts When Using Fakes

Fakes are simplified implementations that mimic real systems, such as an in-memory database or fake email service. But improper design can turn fakes into overengineered, hard-to-maintain test systems.

DoDon’t
Use fakes for dependencies like DBs, queues, or file systemsUse fakes where a stub or mock would suffice
Keep fakes lightweight and fastImplement business logic or complex flows inside fakes
Reset fake state between tests to avoid data leaksShare mutable fake instances across tests without cleanup
Design fakes to simulate basic success/failure conditionsAdd network latency or randomness that slows down tests
Build reusable fake factories for consistencyHardcode fakes directly into individual test files

How Requestly Supports Mocks, Stubs, and Fakes

Requestly enables developers and testers to simulate different types of test doubles directly within their browser or local environment. By intercepting, modifying, and rerouting HTTP traffic in real time, it allows teams to validate integrations, control test behavior, and isolate failures without relying on backend changes or deployments.

Use Requestly as a Stub

When an API isn’t ready, returns unstable data, or slows down development, you can stub it using Requestly by returning fixed responses.

  1. Match any API endpoint and return static JSON, headers, or status codes.
  2. Configure different responses for different methods, routes, or query parameters.
  3. Use stubs to isolate frontend tests from backend availability or instability.

Use Requestly as a Mock

When you want to verify how your app behaves based on network interactions, you can mock those interactions and inspect them in real time.

  1. Record and log requests as they occur, including headers and payloads.
  2. Monitor how many times the app calls a specific endpoint or triggers a sequence.
  3. Simulate delays or failures to test retry logic, fallbacks, or error handling.

Use Requestly as a Fake

When you need a simplified but working version of a backend feature, you can fake it by injecting logic into the response.

  1. Add custom JavaScript to shape API responses based on request inputs.
  2. Simulate different behaviors by checking headers, query parameters, or body content.
  3. Build fakes that mimic real services without needing full backend infrastructure.

Conclusion

Mocks, stubs, and fakes are essential tools for building fast, reliable, and focused tests. Each supports a different testing goal, so the choice depends on whether the test validates behavior, output, or integration. When used correctly, these test doubles improve coverage, reduce dependencies, and keep development fast without sacrificing confidence.

author avatar
Rohit Rajpal
Rohit Rajpal is a B2B Content Strategist at BrowserStack, helping SaaS brands build AI-first strategies that drive authority and revenue. He writes about content, strategy, and the future of search in the zero-click era.
Written by
Rohit Rajpal
Rohit Rajpal is a B2B Content Strategist at BrowserStack, helping SaaS brands build AI-first strategies that drive authority and revenue. He writes about content, strategy, and the future of search in the zero-click era.

Related posts