0
# Recording and Playback
1
2
This document covers nock's recording functionality, which captures real HTTP traffic and converts it into fixtures that can be replayed in tests.
3
4
## Recording Interface
5
6
The recorder provides methods to capture HTTP traffic and convert it to nock definitions.
7
8
```javascript { .api }
9
interface Recorder {
10
rec(options?: boolean | RecorderOptions): void;
11
clear(): void;
12
play(): string[] | Definition[];
13
}
14
15
const recorder: Recorder;
16
function restore(): void;
17
```
18
19
## Basic Recording
20
21
### Start Recording
22
23
```javascript { .api }
24
recorder.rec(options?: boolean | RecorderOptions): void;
25
```
26
27
Start recording HTTP traffic:
28
29
```javascript
30
const nock = require("nock");
31
32
// Start recording with default options
33
nock.recorder.rec();
34
35
// Make real HTTP requests - these will be recorded
36
const response = await fetch("https://api.example.com/users");
37
const data = await response.json();
38
39
// Stop recording and get the recorded fixtures
40
const fixtures = nock.recorder.play();
41
console.log(fixtures);
42
```
43
44
### Simple Boolean Options
45
46
```javascript
47
// Record with logging enabled
48
nock.recorder.rec(true);
49
50
// Record without logging
51
nock.recorder.rec(false);
52
```
53
54
## Recording Options
55
56
```javascript { .api }
57
interface RecorderOptions {
58
dont_print?: boolean;
59
output_objects?: boolean;
60
enable_reqheaders_recording?: boolean;
61
logging?: (content: string) => void;
62
use_separator?: boolean;
63
}
64
```
65
66
### Don't Print Option
67
68
Control whether recorded fixtures are automatically logged to console:
69
70
```javascript
71
// Don't print fixtures automatically (default: false)
72
nock.recorder.rec({ dont_print: true });
73
74
// Make requests...
75
const fixtures = nock.recorder.play(); // Get fixtures manually
76
```
77
78
### Output Objects Option
79
80
Control the format of recorded fixtures:
81
82
```javascript
83
// Output as JavaScript objects (default: false, outputs as strings)
84
nock.recorder.rec({ output_objects: true });
85
86
// Make requests...
87
const fixtures = nock.recorder.play();
88
// fixtures will be Definition[] instead of string[]
89
```
90
91
### Enable Request Headers Recording
92
93
Include request headers in recorded fixtures:
94
95
```javascript
96
nock.recorder.rec({ enable_reqheaders_recording: true });
97
98
// The recorded fixtures will include the headers sent in requests
99
```
100
101
Example output with headers:
102
103
```javascript
104
nock("https://api.example.com")
105
.get("/users")
106
.matchHeader("authorization", "Bearer token123")
107
.matchHeader("user-agent", "Node.js")
108
.reply(200, [{ id: 1, name: "Alice" }]);
109
```
110
111
### Custom Logging
112
113
Provide a custom logging function:
114
115
```javascript
116
const fs = require("fs");
117
118
nock.recorder.rec({
119
logging: (content) => {
120
// Write to file instead of console
121
fs.appendFileSync("./recorded-fixtures.js", content + "\n");
122
}
123
});
124
```
125
126
### Use Separator
127
128
Add separators between recorded fixtures:
129
130
```javascript
131
nock.recorder.rec({ use_separator: true });
132
133
// Output will include separator comments between fixtures:
134
// <<<<<<-- cut here -->>>>>>
135
```
136
137
## Managing Recordings
138
139
### Clear Recordings
140
141
```javascript { .api }
142
recorder.clear(): void;
143
```
144
145
Clear all recorded fixtures without stopping recording:
146
147
```javascript
148
nock.recorder.rec();
149
150
// Make some requests...
151
console.log(nock.recorder.play().length); // e.g., 3
152
153
// Clear recorded data
154
nock.recorder.clear();
155
console.log(nock.recorder.play().length); // 0
156
157
// Continue recording new requests...
158
```
159
160
### Play Recordings
161
162
```javascript { .api }
163
recorder.play(): string[] | Definition[];
164
```
165
166
Get recorded fixtures and optionally stop recording:
167
168
```javascript
169
// Get fixtures as strings (default)
170
const stringFixtures = nock.recorder.play();
171
console.log(stringFixtures[0]);
172
// Output: 'nock("https://api.example.com").get("/users").reply(200, [...]);'
173
174
// Get fixtures as objects
175
nock.recorder.rec({ output_objects: true });
176
// Make requests...
177
const objectFixtures = nock.recorder.play();
178
console.log(objectFixtures[0]);
179
// Output: { scope: "https://api.example.com", method: "GET", path: "/users", ... }
180
```
181
182
### Restore Original HTTP
183
184
```javascript { .api }
185
function restore(): void;
186
```
187
188
Stop recording and restore original HTTP behavior:
189
190
```javascript
191
nock.recorder.rec();
192
// Make requests...
193
194
// Stop recording and restore normal HTTP
195
nock.restore();
196
197
// HTTP requests will now behave normally (not recorded)
198
```
199
200
## Definition Objects
201
202
When using `output_objects: true`, recordings return Definition objects:
203
204
```javascript { .api }
205
interface Definition {
206
scope: string | RegExp;
207
path: string | RegExp;
208
port?: number | string;
209
method?: string;
210
status?: number;
211
body?: RequestBodyMatcher;
212
reqheaders?: Record<string, RequestHeaderMatcher>;
213
response?: ReplyBody;
214
headers?: ReplyHeaders;
215
options?: Options;
216
}
217
```
218
219
### Working with Definition Objects
220
221
```javascript
222
nock.recorder.rec({ output_objects: true });
223
224
// Make a request
225
await fetch("https://api.example.com/users", {
226
method: "POST",
227
headers: { "Content-Type": "application/json" },
228
body: JSON.stringify({ name: "Alice" })
229
});
230
231
const definitions = nock.recorder.play();
232
console.log(definitions[0]);
233
// Output:
234
// {
235
// scope: "https://api.example.com:443",
236
// method: "POST",
237
// path: "/users",
238
// body: '{"name":"Alice"}',
239
// status: 201,
240
// response: '{"id":1,"name":"Alice"}',
241
// headers: { "content-type": "application/json" }
242
// }
243
244
// Convert back to nock interceptors
245
const scopes = nock.define(definitions);
246
```
247
248
## Complete Recording Workflow
249
250
### Record and Generate Test Fixtures
251
252
```javascript
253
const nock = require("nock");
254
const fs = require("fs");
255
256
async function recordAPIInteractions() {
257
// Start recording with comprehensive options
258
nock.recorder.rec({
259
output_objects: true,
260
enable_reqheaders_recording: true,
261
dont_print: true
262
});
263
264
try {
265
// Make real API calls
266
const response1 = await fetch("https://api.example.com/users");
267
const users = await response1.json();
268
269
const response2 = await fetch("https://api.example.com/users/1");
270
const user = await response2.json();
271
272
const response3 = await fetch("https://api.example.com/users", {
273
method: "POST",
274
headers: { "Content-Type": "application/json" },
275
body: JSON.stringify({ name: "Bob" })
276
});
277
const newUser = await response3.json();
278
279
} finally {
280
// Get recorded fixtures
281
const definitions = nock.recorder.play();
282
283
// Save to file
284
const fixtures = JSON.stringify(definitions, null, 2);
285
fs.writeFileSync("./test/fixtures/api-fixtures.json", fixtures);
286
287
// Stop recording
288
nock.restore();
289
}
290
}
291
```
292
293
### Load and Use Recorded Fixtures
294
295
```javascript
296
const nock = require("nock");
297
const fixtures = require("./fixtures/api-fixtures.json");
298
299
describe("API Tests with Recorded Fixtures", () => {
300
beforeEach(() => {
301
// Load recorded fixtures
302
nock.define(fixtures);
303
});
304
305
afterEach(() => {
306
nock.cleanAll();
307
});
308
309
it("should get users list", async () => {
310
// This will use the recorded response
311
const response = await fetch("https://api.example.com/users");
312
const users = await response.json();
313
314
expect(Array.isArray(users)).toBe(true);
315
});
316
});
317
```
318
319
## Advanced Recording Patterns
320
321
### Conditional Recording
322
323
```javascript
324
// Only record in development mode
325
if (process.env.NODE_ENV === "development") {
326
nock.recorder.rec({
327
output_objects: true,
328
logging: (content) => {
329
console.log("Recorded fixture:", content);
330
}
331
});
332
}
333
```
334
335
### Filtering Recorded Requests
336
337
```javascript
338
nock.recorder.rec({ output_objects: true });
339
340
// Make various requests...
341
342
const allDefinitions = nock.recorder.play();
343
344
// Filter to only record API calls to specific domain
345
const apiDefinitions = allDefinitions.filter(def =>
346
def.scope.includes("api.example.com")
347
);
348
349
// Save filtered definitions
350
nock.define(apiDefinitions);
351
```
352
353
### Recording with Custom Processing
354
355
```javascript
356
const processedFixtures = [];
357
358
nock.recorder.rec({
359
output_objects: true,
360
dont_print: true,
361
logging: (content) => {
362
// Process each fixture as it's recorded
363
try {
364
const definition = JSON.parse(content);
365
366
// Remove sensitive headers
367
if (definition.reqheaders) {
368
delete definition.reqheaders.authorization;
369
delete definition.reqheaders.cookie;
370
}
371
372
// Sanitize response data
373
if (definition.response && typeof definition.response === "string") {
374
const response = JSON.parse(definition.response);
375
if (response.email) {
376
response.email = "user@example.com"; // Sanitize email
377
}
378
definition.response = JSON.stringify(response);
379
}
380
381
processedFixtures.push(definition);
382
} catch (e) {
383
// Handle non-JSON fixtures
384
processedFixtures.push(content);
385
}
386
}
387
});
388
```
389
390
## Integration with Test Frameworks
391
392
### Jest Integration
393
394
```javascript
395
// jest.setup.js
396
const nock = require("nock");
397
398
// Global setup for recording
399
global.startRecording = (options = {}) => {
400
nock.recorder.rec({
401
output_objects: true,
402
dont_print: true,
403
...options
404
});
405
};
406
407
global.stopRecording = () => {
408
const fixtures = nock.recorder.play();
409
nock.restore();
410
return fixtures;
411
};
412
413
// jest.teardown.js
414
afterEach(() => {
415
if (nock.recorder.play().length > 0) {
416
nock.restore();
417
}
418
nock.cleanAll();
419
});
420
```
421
422
### Mocha Integration
423
424
```javascript
425
const nock = require("nock");
426
427
describe("API Tests", () => {
428
let recordedFixtures = [];
429
430
before(() => {
431
// Start recording for entire test suite
432
nock.recorder.rec({ output_objects: true });
433
});
434
435
after(() => {
436
// Save all recorded fixtures
437
recordedFixtures = nock.recorder.play();
438
nock.restore();
439
440
// Optionally save to file
441
if (process.env.SAVE_FIXTURES) {
442
const fs = require("fs");
443
fs.writeFileSync(
444
"./test/recorded-fixtures.json",
445
JSON.stringify(recordedFixtures, null, 2)
446
);
447
}
448
});
449
450
// Individual tests...
451
});
452
```
453
454
## Troubleshooting Recording
455
456
### Common Issues
457
458
1. **Headers not recorded**: Use `enable_reqheaders_recording: true`
459
2. **Binary data issues**: Ensure proper encoding for binary responses
460
3. **Too many fixtures**: Use `clear()` periodically or filter recordings
461
4. **Sensitive data**: Always sanitize recorded fixtures before saving
462
463
### Debugging Recording
464
465
```javascript
466
nock.recorder.rec({
467
logging: (content) => {
468
console.log("Recording:", content);
469
470
// Debug the structure
471
if (content.startsWith("{")) {
472
try {
473
const parsed = JSON.parse(content);
474
console.log("Parsed definition:", parsed);
475
} catch (e) {
476
console.log("Failed to parse:", e.message);
477
}
478
}
479
}
480
});
481
```