Nock Interceptors
Basic Interceptors
const nock = require('nock');
const axios = require('axios');
// GET request
nock('https://api.example.com')
.get('/users')
.reply(200, [{ id: 1, name: 'Alice' }]);
// Test
const { data } = await axios.get('https://api.example.com/users');
console.log(data); // [{ id: 1, name: 'Alice' }]
// With query string
nock('https://api.example.com')
.get('/search')
.query({ q: 'test', page: '1' })
.reply(200, { results: [] });
// POST with body matching
nock('https://api.example.com')
.post('/users', { name: 'Bob', email: '[email protected]' })
.reply(201, { id: 2, name: 'Bob' });
// PUT / DELETE
nock('https://api.example.com')
.put('/users/1', { name: 'Alice Updated' })
.reply(200, { id: 1, name: 'Alice Updated' });
nock('https://api.example.com')
.delete('/users/1')
.reply(204);
Reply Bodies & Status Codes
// JSON body (auto Content-Type: application/json)
nock('https://api.example.com')
.get('/data')
.reply(200, { items: [1, 2, 3] });
// Custom headers
nock('https://api.example.com')
.get('/users')
.reply(200, [{ id: 1 }], {
'X-Total-Count': '42',
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
});
// Error responses
nock('https://api.example.com')
.get('/protected')
.reply(401, { error: 'Unauthorized' });
nock('https://api.example.com')
.get('/missing')
.reply(404, { message: 'Not Found' });
// Network error (simulate connection failure)
nock('https://api.example.com')
.get('/broken')
.replyWithError('ECONNRESET');
// Error with code
nock('https://api.example.com')
.get('/timeout')
.replyWithError({ message: 'Timeout', code: 'ETIMEDOUT' });
// Delay response
nock('https://api.example.com')
.get('/slow')
.delay(500)
.reply(200, { data: 'slow' });
Scope Chaining & Persistence
// Chain multiple intercepts on same host
nock('https://api.example.com')
.get('/users')
.reply(200, [{ id: 1 }])
.post('/users')
.reply(201, { id: 2 })
.delete('/users/1')
.reply(204);
// Intercept same path multiple times
nock('https://api.example.com')
.get('/status')
.reply(503) // first call
.get('/status')
.reply(200, 'ok'); // second call
// Persist indefinitely (use for all tests in suite)
nock('https://api.example.com')
.get('/config')
.reply(200, { env: 'test' })
.persist(); // never consumed
// Times โ respond N times then stop
nock('https://api.example.com')
.get('/items')
.times(3)
.reply(200, []);
// Intercept with path regex
nock('https://api.example.com')
.get(/\/users\/\d+/)
.reply(200, { id: 1, name: 'Any User' });
Request Matching โ Body & Headers
// Match body with partial object
nock('https://api.example.com')
.post('/users', body => {
return body.role === 'admin'; // custom matcher function
})
.reply(201, { id: 1, role: 'admin' });
// Match headers
nock('https://api.example.com', {
reqheaders: {
'authorization': 'Bearer mytoken',
'content-type': /application\/json/,
'x-api-version': val => val === '2',
},
})
.get('/secure')
.reply(200, { secret: 'data' });
// Allow all headers (default)
// Bad header match = interceptor not triggered = real request made
// = Error: Nock: No match for request
// Match with body regex
nock('https://api.example.com')
.post('/email', /confirm/)
.reply(200);
nock.back โ Record & Replay
const nock = require('nock');
const path = require('path');
// Set fixture directory
nock.back.fixtures = path.join(__dirname, 'fixtures');
nock.back.setMode('record'); // modes: record, playback, wild, dryrun
test('records real API response', async () => {
const { nockDone } = await nock.back('github-users.json');
const response = await fetch('https://api.github.com/users/octocat');
const data = await response.json();
nockDone(); // saves recording if not exists, verifies if exists
expect(data.login).toBe('octocat');
});
// Modes:
// record โ record if fixture missing, playback if exists
// playback โ only use fixture (fail if missing)
// wild โ disable interceptors (real requests always)
// dryrun โ playback if fixture exists, else allow real request
Cleanup & Verification
// Cleanup after tests
afterEach(() => {
nock.cleanAll(); // remove all interceptors
nock.restore(); // restore http module
nock.activate(); // re-activate nock
});
// Verify all interceptors were used
test('all mocks were called', async () => {
const scope = nock('https://api.example.com')
.get('/users').reply(200, [])
.get('/config').reply(200, {});
await Promise.all([
axios.get('https://api.example.com/users'),
axios.get('https://api.example.com/config'),
]);
expect(scope.isDone()).toBe(true); // all interceptors consumed
expect(nock.pendingMocks()).toHaveLength(0);
});
// Disable nock globally (allow real requests)
nock.enableNetConnect('localhost'); // allow only localhost
nock.enableNetConnect(/example\.com/); // allow pattern
nock.disableNetConnect(); // block all (default when interceptor exists)
// Debug pending interceptors
console.log(nock.pendingMocks());
console.log(nock.activeMocks());