How to Mock in Integration Tests: Tools and Implementation
Mock smarter in integration testing. Explore tools like Nock, Sinon, and Jest with hands-on examples and a step-by-step guide to accurate and efficient mocks.

Having established when and why to mock in integration tests, it's time to explore the practical implementation of mocking strategies. This comprehensive guide covers the essential tools, techniques, and step-by-step processes for implementing effective mocks in your integration tests. We'll dive deep into popular mocking libraries, provide detailed examples, and explore advanced techniques for maintaining reliable and accurate mocks.
ESSENTIAL MOCKING TOOLS AND LIBRARIES
The JavaScript ecosystem offers several powerful tools for implementing mocks in integration tests. Each tool serves different purposes and excels in specific scenarios. Understanding their strengths and use cases will help you choose the right tool for your specific testing needs.
NOCK: HTTP Request Mocking
Nock is the most popular and powerful HTTP request mocking library for Node.js. It allows you to intercept and mock HTTP requests at the network level, making it ideal for testing applications that interact with external APIs.
Key Features:
- Intercepts HTTP requests at the network level
- Supports complex request matching patterns
- Provides detailed request/response validation
- Offers recording and playback capabilities
- Integrates seamlessly with all testing frameworks
- Supports both REST and GraphQL APIs
When to Use Nock:
- Testing interactions with external REST APIs
- Mocking third-party services like payment gateways
- Testing error handling for HTTP failures
- Validating request formats and headers
- Creating deterministic responses for external services
Basic Nock Usage:
const nock = require('nock');
// Mock a GET request
nock('https://api.example.com')
.get('/users/1')
.reply(200, {
id: 1,
name: 'John Doe',
email: '[email protected]'
});
// Mock a POST request with body matching
nock('https://api.example.com')
.post('/users', { name: 'Jane Doe', email: '[email protected]' })
.reply(201, { id: 2, name: 'Jane Doe' });
// Mock with headers
nock('https://api.example.com')
.get('/protected-resource')
.matchHeader('Authorization', 'Bearer my-token')
.reply(200, { data: 'secret' });
// Mock an error response
nock('https://api.example.com')
.get('/flaky-endpoint')
.replyWithError('Connection refused');
// Clean up after tests
afterEach(() => {
nock.cleanAll();
});
SINON: Comprehensive Function Mocking
Sinon is a versatile library that provides spies, stubs, and mocks for JavaScript functions and objects. It's particularly useful for mocking internal dependencies and complex object interactions.
Key Features:
- Function spies for monitoring calls
- Stubs for replacing function behavior
- Mocks for complex object interactions
- Fake timers for time-based testing
- Extensive assertion capabilities
- Works with any testing framework
When to Use Sinon:
- Mocking internal service dependencies
- Testing time-based functionality
- Spying on function calls and arguments
- Stubbing complex object methods
- Testing callback and promise behavior
Basic Sinon Usage:
const sinon = require('sinon');
describe('UserService', () => {
let sandbox;
beforeEach(() => {
sandbox = sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
});
it('should call the email service on registration', async () => {
const emailSpy = sandbox.spy(emailService, 'sendWelcomeEmail');
await userService.register({ name: 'Alice', email: '[email protected]' });
sinon.assert.calledOnce(emailSpy);
sinon.assert.calledWith(emailSpy, '[email protected]');
});
it('should handle email service failure gracefully', async () => {
sandbox.stub(emailService, 'sendWelcomeEmail').rejects(new Error('SMTP error'));
const result = await userService.register({ name: 'Bob', email: '[email protected]' });
expect(result.status).toBe('registered_without_email');
});
it('should expire tokens after timeout', async () => {
const clock = sandbox.useFakeTimers();
const token = await authService.createToken('user-123');
clock.tick(3600 * 1000); // Advance 1 hour
const isValid = await authService.validateToken(token);
expect(isValid).toBe(false);
clock.restore();
});
});
JEST BUILT-IN MOCKING
Jest provides powerful built-in mocking capabilities that integrate seamlessly with the testing framework. While not as specialized as Nock or Sinon, Jest mocking is convenient for simple scenarios and module-level mocking.
Key Features:
- Module mocking with automatic mock generation
- Function mocking with call tracking
- Timer mocking for time-based tests
- Snapshot testing for mocked responses
- Mock clearing and restoration utilities
When to Use Jest Mocking:
- Simple function mocking scenarios
- Module-level mocking
- Quick prototyping of mocks
- Integration with Jest snapshot testing
Basic Jest Mocking:
jest.mock('./paymentGateway', () => ({
charge: jest.fn().mockResolvedValue({ success: true, transactionId: 'txn_123' }),
refund: jest.fn().mockResolvedValue({ success: true }),
}));
describe('OrderService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should process payment on order placement', async () => {
const { charge } = require('./paymentGateway');
await orderService.placeOrder({ userId: 1, total: 99.99 });
expect(charge).toHaveBeenCalledWith({
amount: 99.99,
currency: 'USD',
userId: 1,
});
});
it('should handle payment failure', async () => {
const { charge } = require('./paymentGateway');
charge.mockRejectedValueOnce(new Error('Card declined'));
await expect(
orderService.placeOrder({ userId: 1, total: 99.99 })
).rejects.toThrow('Payment failed');
});
});
STEP-BY-STEP IMPLEMENTATION GUIDE
Let's walk through a comprehensive example implementing integration tests with strategic mocking for a user notification system. Our example includes a NotificationService that orchestrates email and SMS delivery via external providers, a UserRepository for database access, and a NotificationLog for audit records.
Step 1: Test Environment Setup
// test/setup.js
const nock = require('nock');
const { MongoMemoryServer } = require('mongodb-memory-server');
let mongoServer;
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
process.env.MONGO_URI = mongoServer.getUri();
nock.disableNetConnect();
nock.enableNetConnect('127.0.0.1');
});
afterAll(async () => {
await mongoServer.stop();
nock.enableNetConnect();
});
afterEach(() => {
nock.cleanAll();
});
Step 2: Basic Integration Test with Mocking
// test/notification.integration.test.js
const request = require('supertest');
const nock = require('nock');
const app = require('../src/app');
const { seedUser } = require('./helpers/seed');
describe('POST /notifications/send', () => {
let user;
beforeEach(async () => {
user = await seedUser({ email: '[email protected]', phone: '+1234567890' });
});
it('should send email and SMS notifications successfully', async () => {
nock('https://api.sendgrid.com')
.post('/v3/mail/send')
.reply(202, { message: 'Accepted' });
nock('https://api.twilio.com')
.post(`/2010-04-01/Accounts/${process.env.TWILIO_SID}/Messages.json`)
.reply(201, { sid: 'SM123', status: 'queued' });
const response = await request(app)
.post('/notifications/send')
.send({ userId: user.id, message: 'Your order has shipped!' })
.set('Authorization', `Bearer ${process.env.TEST_API_KEY}`);
expect(response.status).toBe(200);
expect(response.body).toMatchObject({
email: { status: 'sent' },
sms: { status: 'queued' },
});
});
it('should still send SMS if email provider fails', async () => {
nock('https://api.sendgrid.com')
.post('/v3/mail/send')
.reply(500, { error: 'Internal Server Error' });
nock('https://api.twilio.com')
.post(`/2010-04-01/Accounts/${process.env.TWILIO_SID}/Messages.json`)
.reply(201, { sid: 'SM456', status: 'queued' });
const response = await request(app)
.post('/notifications/send')
.send({ userId: user.id, message: 'Your order has shipped!' })
.set('Authorization', `Bearer ${process.env.TEST_API_KEY}`);
expect(response.status).toBe(207);
expect(response.body.email.status).toBe('failed');
expect(response.body.sms.status).toBe('queued');
});
});
Step 3: Advanced Mocking Scenarios
Dynamic Response Generation — respond differently based on request content:
nock('https://api.sendgrid.com')
.post('/v3/mail/send', (body) => body.to === '[email protected]')
.reply(202, { priority: 'high' });
nock('https://api.sendgrid.com')
.post('/v3/mail/send')
.reply(202, { priority: 'normal' });
Simulating Rate Limits:
nock('https://api.sendgrid.com')
.post('/v3/mail/send')
.times(3)
.reply(202)
.post('/v3/mail/send')
.reply(429, { error: 'Too Many Requests' });
Step 4: Performance Testing with Mocks
it('should handle 100 concurrent notification requests', async () => {
nock('https://api.sendgrid.com')
.post('/v3/mail/send')
.times(100)
.reply(202);
nock('https://api.twilio.com')
.post(/Messages\.json$/)
.times(100)
.reply(201, { status: 'queued' });
const requests = Array.from({ length: 100 }, (_, i) =>
request(app)
.post('/notifications/send')
.send({ userId: `user-${i}`, message: 'Test notification' })
.set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
);
const start = Date.now();
const responses = await Promise.all(requests);
const duration = Date.now() - start;
expect(responses.every((r) => r.status === 200)).toBe(true);
expect(duration).toBeLessThan(5000);
});
ADVANCED MOCKING TECHNIQUES
Request Recording and Playback
Use Nock's record mode to capture real API responses once and replay them in all future test runs:
// Record mode — run once against real APIs
if (process.env.RECORD_MOCKS === 'true') {
nock.recorder.rec({ output_objects: true });
}
// Playback mode (default)
const recordings = require('./fixtures/api-recordings.json');
nock.define(recordings);
Mock Validation
Always verify that all registered mocks were called — this surfaces stale or unused mocks:
afterEach(() => {
expect(nock.pendingMocks()).toHaveLength(0);
nock.cleanAll();
});
BEST PRACTICES FOR MOCK IMPLEMENTATION
1. Keep Mocks Simple and Focused
Each mock should represent exactly one scenario. Avoid embedding complex conditional logic inside a mock — create separate test cases instead.
2. Use Realistic Data and Delays
Make mocks representative of real system behavior. Add simulated latency where it matters:
nock('https://api.sendgrid.com')
.post('/v3/mail/send')
.delay(120)
.reply(202);
3. Mock at the Appropriate Level
- Use Nock for external HTTP services
- Use Sinon stubs for internal module dependencies
- Use in-memory databases rather than mocking the database layer
4. Document Mock Decisions
Always leave a comment explaining why a mock was introduced and when it should be revisited:
// Mocking SendGrid because CI runs against the free tier which has strict
// rate limits. Revisit if we move to a paid plan — real integration tests
// would be preferable.
nock('https://api.sendgrid.com')
.post('/v3/mail/send')
.reply(202);
CONCLUSION
Implementing effective mocks in integration tests requires careful consideration of tools, techniques, and maintenance strategies. The key is to use mocks strategically — eliminating problematic external dependencies while preserving the authentic integrations that provide the most value.
Nock excels at HTTP request mocking and is essential for testing external API integrations. Sinon provides comprehensive function and object mocking for internal dependencies. Jest's built-in mocking works well for simple scenarios and integrates seamlessly with the testing framework.
The examples in this guide demonstrate how to implement mocks that are realistic, maintainable, and provide genuine testing value. By following these patterns and best practices, you can create integration tests that give you confidence in your system's behavior while remaining practical to execute.
Remember: mocks are tools to enable effective testing, not goals in themselves. Regularly review and update your mocking strategies as your application evolves to ensure they continue to accurately represent the systems they replace.
---
Originally published on GeekyAnts Blog: https://geekyants.com/blog/how-to-mock-in-integration-tests-tools-and-implementation
Author: Nilesh Kumar, Software Engineer II at GeekyAnts (https://geekyants.com)
About the Creator
Geekyants
GeekyAnts is a global technology consulting and product development company specializing in digital transformation, custom software solutions, and end-to-end app development.
Enjoyed the story? Support the Creator.
Subscribe for free to receive all their stories in your feed.
Comments
There are no comments for this story
Be the first to respond and start the conversation.