Mock vs Stub: Understanding the Key Differences

keploy - Oct 16 - - Dev Community

Image description
In software testing, mock vs stub are two popular types of test doubles used to simulate dependencies. They help isolate the behavior of a component by replacing real dependencies with controlled substitutes during testing. While both mocks and stubs serve similar purposes, they are used differently based on the type of testing scenario.
This article dives deep into the key differences between mocks and stubs, their use cases, and when to use each in your testing strategy.

What is a Test Double?
A test double is a generic term for any object or component used to stand in for a real dependency during testing. The goal is to isolate the component under test and remove external factors that could affect the outcome of the test. Mocks and stubs are two types of test doubles, often used in unit and integration testing.

What is a Stub?
A stub is a test double that returns predefined data when called by the component under test. It is typically used when the component being tested requires input from a dependency, but the actual behavior of the dependency is irrelevant to the test outcome. Stubs provide controlled responses, allowing you to focus only on the logic inside the component.

Example of a Stub:
Let’s say you’re testing a payment processing function. Instead of calling a real payment gateway, you use a stub that always returns a "payment successful" response, ensuring the logic in the function can be tested independently.

const paymentGatewayStub = {
  processPayment: () => "payment successful"
};

function processOrder(paymentService) {
  const result = paymentService.processPayment();
  return result === "payment successful" ? "Order Complete" : "Order Failed";
}

// Test
console.log(processOrder(paymentGatewayStub));  // Output: "Order Complete"
Enter fullscreen mode Exit fullscreen mode

In this case, the stub ensures that the external service always returns the expected output, making it easier to test the internal logic.

What is a Mock?
A mock is a test double that not only provides fake data but also verifies interactions between components. Mocks help ensure that the correct methods are called with the right parameters and that the desired sequence of events occurs during execution. They are typically used when the behavior or interaction of the component matters to the test.
Example of a Mock:
Using the same payment example, let’s say you want to verify that the processPayment() method is called exactly once during order processing.

const paymentGatewayMock = {
  processPayment: jest.fn().mockReturnValue("payment successful")
};

function processOrder(paymentService) {
  paymentService.processPayment();  
}

// Test
processOrder(paymentGatewayMock);
expect(paymentGatewayMock.processPayment).toHaveBeenCalledTimes(1);
Enter fullscreen mode Exit fullscreen mode

In this case, the mock verifies the interaction by checking if the method was called, ensuring that the component behaves correctly during execution.
Key Differences Between Mock and Stub
Aspect Mock Stub
Purpose Verifies interactions between components Provides predefined responses
Behavior Validates method calls and parameters Only returns static data
Usage Scenario Used when interaction matters Used when data output is enough
Complexity More complex to implement Simpler to create
Example Test Verifying method calls Testing logic with fixed responses

When to Use a Stub
• Testing Simple Logic: Use a stub when you only need to control the output of a dependency.
• Data-Driven Tests: If the focus is on testing how your component behaves with specific data, stubs are more suitable.
• Example Use Case: If you are testing a login feature that needs user data from a database, use a stub to return a dummy user without calling the real database.
When to Use a Mock
• Interaction-Based Testing: If the focus is on ensuring that the correct methods are called with the right parameters, use a mock.
• Testing Complex Interactions: Mocks are ideal for components that interact with multiple services or APIs.
• Example Use Case: If you are testing an email-sending service, use a mock to verify that the sendEmail() function was called with the expected recipient and message.
Can Mocks and Stubs Be Used Together?
Yes, in some cases, mocks and stubs are used together within the same test to achieve both interaction validation and controlled responses. For example, you might use a stub to return specific data and a mock to verify that a particular service was called.
Pros and Cons of Mocks and Stubs
Pros of Stubs:
• Simple and easy to implement
• Useful for testing component logic in isolation
Cons of Stubs:
• Cannot validate method calls or parameters
• Limited to static responses
Pros of Mocks:
• Verifies behavior, interactions, and sequences
• Useful for complex scenarios with multiple dependencies
Cons of Mocks:
• Requires more setup and can make tests harder to read
• Can lead to fragile tests if not used carefully
Mock vs Stub: Which One Should You Use?
• If your test depends on the output of a dependency and you want to control that output, use a stub.
• If you need to verify that a method was called or validate interactions between components, use a mock.
• For unit testing, stubs are more commonly used since the focus is on testing internal logic.
• For integration testing, mocks are more useful when verifying how different parts of the system interact.

Conclusion
Both mocks and stubs play essential roles in testing strategies, but each serves a different purpose. While stubs control the data a dependency returns, mocks ensure that the correct interactions happen between components. Understanding when and how to use each can improve the quality of your tests, leading to more reliable software. By applying mocks and stubs correctly, teams can reduce bugs, speed up development, and create better testing strategies for both unit and integration tests.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .