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());