Mock vs Stub vs Fake: Core differences


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.
- Purpose: Control the return value or output of a function or service call.
- Behavior: Passive; only responds as configured and does not record how it is used.
- 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.
- Purpose: Validate that interactions happened as expected.
- Behavior: Active; includes built-in mechanisms to record and assert calls.
- 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.
- Purpose: Provide functional logic for integration without external dependencies.
- Behavior: Executes real operations using fake or local infrastructure.
- 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:
- 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.
- 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.
- 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.
- 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.
- 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:
- 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.
- Removes dependency unpredictability: When real APIs fluctuate due to data changes or rate limits, stubs provide consistent and reproducible outputs.
- 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.
- Accelerates test development: When upstream services are unavailable or under construction, stubs allow downstream teams to write tests and implement logic in parallel.
- 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:
- 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.
- 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.
- 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.
- 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.
- 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.
| Aspect | Stub | Mock | Fake |
| Primary purpose | Control return values | Verify that interactions occurred | Provide working but simplified implementation |
| Focus of test | Outcome-based logic | Interaction and behavior | End-to-end logic using test-safe versions |
| Implements real logic | No | No | Yes |
| Tracks method calls | No | Yes | Sometimes, for debugging |
| Verification support | Not supported | Built-in (assertions, call tracking) | Indirect, through behavior or side effects |
| Complexity level | Very low | Medium (due to configuration and verification) | Medium to high (real code, needs to be maintained) |
| Execution path coverage | Single-path or branch-specific testing | Triggered when testing alternate flows, retries, fallbacks | Supports realistic flow across multiple components |
| Dependency simulation style | Static response | Dynamic observation | Real behavior without production overhead |
| Test scope suitability | Unit tests | Unit or contract-style interaction tests | Integration, end-to-end, or acceptance tests |
| Best for testing | Decision branches, error handling | Notification triggers, logging behavior, interaction count | Data flow, persistence, business logic flow |
| Risk if misused | Masked logic bugs due to oversimplified responses | Brittle tests that fail on non-essential interaction changes | Overconfidence in test coverage if fake diverges from real impl. |
| Ideal development stage | Early logic development | Mid-stage behavior testing | Later-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:
- Validates input and stores user data
- Sends a verification email
- 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.
| Do | Don’t |
| Use mocks to verify external interactions like logging or event emissions | Use mocks to simulate return values—use stubs instead |
| Keep interaction expectations specific and meaningful | Oververify every method call or parameter unnecessarily |
| Isolate mocks to the unit being tested | Let 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 behavior | Assert 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.
| Do | Don’t |
| Use stubs to simulate external systems returning fixed responses | Use stubs to track how many times a method was called |
| Keep return values simple and deterministic | Add conditional logic or branching to stubs |
| Use stubs to trigger success/failure paths | Mix stubs and mocks in the same test unless necessary |
| Document why the stub is used and what it controls | Let stubs silently affect unrelated parts of the system |
| Stub interfaces or contracts, not internal utility methods | Stub 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.
| Do | Don’t |
| Use fakes for dependencies like DBs, queues, or file systems | Use fakes where a stub or mock would suffice |
| Keep fakes lightweight and fast | Implement business logic or complex flows inside fakes |
| Reset fake state between tests to avoid data leaks | Share mutable fake instances across tests without cleanup |
| Design fakes to simulate basic success/failure conditions | Add network latency or randomness that slows down tests |
| Build reusable fake factories for consistency | Hardcode 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.
- Match any API endpoint and return static JSON, headers, or status codes.
- Configure different responses for different methods, routes, or query parameters.
- 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.
- Record and log requests as they occur, including headers and payloads.
- Monitor how many times the app calls a specific endpoint or triggers a sequence.
- 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.
- Add custom JavaScript to shape API responses based on request inputs.
- Simulate different behaviors by checking headers, query parameters, or body content.
- 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.


Contents
- What Are Mocks, Stubs, and Fakes?
- What is a Stub
- What is a Mock
- What is a Fake
- Why Use Mocks, Stubs, and Fakes in Testing?
- Benefits of Using Mocks
- Benefits of Using Stubs
- Benefits of Using Fakes
- Key Differences Between Mocks, Stubs, and Fakes
- Examples of Using Mocks, Stubs, and Fakes
- 1. Using Mocks
- 2. Using Stubs
- 3. Using Fakes
- Dos and Don’ts When Using Test Doubles
- Dos and Don’ts When Using Mocks
- Dos and Don’ts When Using Stubs
- Dos and Don’ts When Using Fakes
- How Requestly Supports Mocks, Stubs, and Fakes
- Use Requestly as a Stub
- Use Requestly as a Mock
- Use Requestly as a Fake
- Conclusion
Subscribe for latest updates
Share this article
Related posts











