Mocking & Stubs
In a real-world application like CodeHarborHub, your code doesn't live in a bubble. It talks to:
- ๐ง Email Services (SendGrid/Nodemailer)
- ๐ณ Payment Gateways (Stripe/Razorpay)
- โ๏ธ Cloud Storage (AWS S3)
- ๐ External APIs (GitHub/Google)
If you use the real services during testing, your tests will be slow, they might cost you money, and they will fail if the internet goes down. We solve this by using Mocks and Stubs.
๐ง Whatโs the Difference?โ
While people often use these terms interchangeably, there is a technical difference:
| Concept | Simple Definition | Analogy |
|---|---|---|
| Stub | A "dumb" object that returns a hardcoded value. | A pre-recorded voicemail message. |
| Mock | A "smart" object that records how it was called. | A spy who reports back: "The target called me twice at 5:00 PM." |
When to use Mocking?โ
You should mock any dependency that is non-deterministic (unpredictable) or external:
- Network Requests: Don't hit a real URL; mock the response.
- Time: If a feature only works on weekends, "mock" the system clock to be a Saturday.
- Randomness: If a function generates a random ID, mock it to always return
123. - Costly Actions: Mocking the "Send Email" function so you don't spam real users during testing.
Mocking with Jestโ
Let's say we have a function that sends a "Course Completion" email to a student.
The Serviceโ
export const sendWelcomeEmail = async (email) => {
// Imagine this calls a real API like SendGrid
const response = await fetch('[https://api.sendgrid.com/v3/send](https://api.sendgrid.com/v3/send)', { ... });
return response.ok;
};
The Testโ
We want to test our signup logic without actually sending an email.
import * as emailService from './emailService';
import { signupUser } from './auth';
// 1. Tell Jest to "hijack" the email service
jest.mock('./emailService');
test('signup should call the email service', async () => {
// 2. Setup the "Mock" to return a successful value
emailService.sendWelcomeEmail.mockResolvedValue(true);
const result = await signupUser('ajay@example.com');
// 3. Assert: Check if the function was CALLED
expect(emailService.sendWelcomeEmail).toHaveBeenCalledTimes(1);
expect(emailService.sendWelcomeEmail).toHaveBeenCalledWith('ajay@example.com');
expect(result.success).toBe(true);
});
The Dangers of Over-Mockingโ
Mocking is powerful, but if you mock everything, your tests become useless.
- Bad: Mocking your own internal database logic in an Integration Test. (You want to test the real DB!)
- Good: Mocking the Stripe API in a Unit Test. (You don't want to charge a real card!)
Rule of Thumb: Mock the things you don't control (3rd party APIs). Don't mock the things you do control (your own logic).
Summary Checklistโ
- I understand that Mocks replace "Real-world" unpredictable services.
- I know that Stubs provide data, while Mocks verify behavior.
- I can use
jest.mock()to fake a module. - I understand that over-mocking can lead to tests that pass even when the app is broken.
Congratulations! You've learned how to build a professional testing suite. From Unit Tests to Mocks, you now have the tools to build a robust, industrial-level backend for CodeHarborHub.