HTTP server mocking and expectations library for Node.js testing environments
67
This document covers how to define mock responses for intercepted HTTP requests using nock's Interceptor interface.
The primary method for defining responses is the reply method, which has several overloads for different use cases.
interface Interceptor {
reply(responseCode?: number, body?: ReplyBody, headers?: ReplyHeaders): Scope;
reply(
replyFn: (uri: string, body: Body) => ReplyFnResult | Promise<ReplyFnResult>
): Scope;
reply(
replyFnWithCallback: (
uri: string,
body: Body,
callback: (err: NodeJS.ErrnoException | null, result: ReplyFnResult) => void
) => void
): Scope;
reply(
statusCode: number,
replyBodyFn: (uri: string, body: Body) => ReplyBody | Promise<ReplyBody>,
headers?: ReplyHeaders
): Scope;
reply(
statusCode: number,
replyBodyFnWithCallback: (
uri: string,
body: Body,
callback: (err: NodeJS.ErrnoException | null, result: ReplyBody) => void
) => void,
headers?: ReplyHeaders
): Scope;
}// Basic 200 OK response
nock("https://api.example.com")
.get("/users")
.reply(200);
// With response body
nock("https://api.example.com")
.get("/users")
.reply(200, [{ id: 1, name: "Alice" }]);
// With custom headers
nock("https://api.example.com")
.get("/users")
.reply(200, [{ id: 1, name: "Alice" }], {
"Content-Type": "application/json",
"X-Total-Count": "1"
});
// Different status codes
nock("https://api.example.com")
.post("/users")
.reply(201, { id: 2, name: "Bob" });
nock("https://api.example.com")
.get("/users/999")
.reply(404, { error: "User not found" });Use functions to generate responses based on the request:
// Function returning response data
nock("https://api.example.com")
.get(/\/users\/(\d+)/)
.reply(200, (uri, requestBody) => {
const id = uri.match(/\/users\/(\d+)/)[1];
return { id: parseInt(id), name: `User ${id}` };
});
// Function returning full response
nock("https://api.example.com")
.post("/users")
.reply((uri, requestBody) => {
const user = JSON.parse(requestBody);
if (!user.name) {
return [400, { error: "Name is required" }];
}
return [201, { ...user, id: Math.floor(Math.random() * 1000) }];
});
// Async function support
nock("https://api.example.com")
.get("/users")
.reply(async (uri, requestBody) => {
// Simulate async operation
await new Promise(resolve => setTimeout(resolve, 100));
return [200, { message: "Users loaded asynchronously" }];
});For more complex scenarios requiring callback-style responses:
nock("https://api.example.com")
.get("/users")
.reply(function(uri, requestBody, callback) {
// 'this' refers to the interceptor context
const req = this.req;
// Simulate async operation
setTimeout(() => {
callback(null, [200, { users: [], requestId: req.headers["x-request-id"] }]);
}, 100);
});type ReplyBody = string | Record<string, any> | Buffer | ReadStream;Nock supports various response body types:
nock("https://api.example.com")
.get("/user")
.reply(200, { id: 1, name: "Alice", active: true });nock("https://api.example.com")
.get("/status")
.reply(200, "Server is healthy");const imageBuffer = Buffer.from("fake-image-data");
nock("https://api.example.com")
.get("/image.png")
.reply(200, imageBuffer, { "Content-Type": "image/png" });const fs = require("fs");
const stream = fs.createReadStream("./test-data.json");
nock("https://api.example.com")
.get("/large-dataset")
.reply(200, stream, { "Content-Type": "application/json" });Generate network-level errors instead of HTTP error responses:
replyWithError(errorMessage: string | object): Scope;// String error message
nock("https://api.example.com")
.get("/users")
.replyWithError("Network timeout");
// Error object
nock("https://api.example.com")
.get("/users")
.replyWithError({
code: "ECONNREFUSED",
message: "Connection refused"
});
// This will cause the HTTP request to fail with the specified errorRespond with the contents of a file:
replyWithFile(statusCode: number, fileName: string, headers?: ReplyHeaders): Scope;nock("https://api.example.com")
.get("/config")
.replyWithFile(200, "./test-fixtures/config.json", {
"Content-Type": "application/json"
});type ReplyHeaders =
| Record<string, ReplyHeaderValue>
| Map<string, ReplyHeaderValue>
| ReplyHeaderValue[];
type ReplyHeaderValue = string | string[] | ReplyHeaderFunction;
type ReplyHeaderFunction = (
req: ClientRequest,
res: IncomingMessage,
body: string | Buffer
) => string | string[];// Object format
nock("https://api.example.com")
.get("/users")
.reply(200, [], {
"Content-Type": "application/json",
"X-Total-Count": "0"
});
// Array format (key-value pairs)
nock("https://api.example.com")
.get("/users")
.reply(200, [], [
"Content-Type", "application/json",
"X-Total-Count", "0"
]);
// Map format
const headers = new Map();
headers.set("Content-Type", "application/json");
headers.set("X-Total-Count", "0");
nock("https://api.example.com")
.get("/users")
.reply(200, [], headers);// Multiple values for same header
nock("https://api.example.com")
.get("/users")
.reply(200, [], {
"Set-Cookie": ["session=abc123", "theme=dark"]
});nock("https://api.example.com")
.get("/users")
.reply(200, [], {
"Content-Type": "application/json",
"X-Request-Time": (req, res, body) => new Date().toISOString(),
"X-Body-Length": (req, res, body) => body.length.toString()
});Control the timing of responses to simulate network latency or slow servers.
delay(ms: number): this;// Delay response by 1 second
nock("https://api.example.com")
.get("/users")
.delay(1000)
.reply(200, []);delay(opts: { head?: number; body?: number }): this;// Separate delays for headers and body
nock("https://api.example.com")
.get("/users")
.delay({
head: 100, // Delay headers by 100ms
body: 500 // Delay body by additional 500ms
})
.reply(200, []);These methods are deprecated but still supported:
delayBody(timeMs: number): this;
delayConnection(timeMs: number): this;// Deprecated - use delay() instead
nock("https://api.example.com")
.get("/users")
.delayConnection(200)
.delayBody(300)
.reply(200, []);Control how many times an interceptor can be matched.
interface Interceptor {
times(newCounter: number): this;
once(): this;
twice(): this;
thrice(): this;
optionally(flag?: boolean): this;
}// Match exactly 3 times
nock("https://api.example.com")
.get("/users")
.times(3)
.reply(200, []);
// Convenience methods
nock("https://api.example.com")
.get("/status")
.once() // Same as .times(1)
.reply(200, "OK");
nock("https://api.example.com")
.get("/health")
.twice() // Same as .times(2)
.reply(200, "Healthy");
nock("https://api.example.com")
.get("/ping")
.thrice() // Same as .times(3)
.reply(200, "Pong");// This interceptor is optional for scope.done() checks
nock("https://api.example.com")
.get("/optional-endpoint")
.optionally()
.reply(200, "Optional response");
// Can also be toggled
nock("https://api.example.com")
.get("/conditional")
.optionally(process.env.NODE_ENV === "test")
.reply(200, "Conditional response");When using function-based replies, the function receives a context with additional information:
interface ReplyFnContext extends Interceptor {
req: ClientRequest & {
headers: Record<string, string>;
};
}nock("https://api.example.com")
.post("/users")
.reply(function(uri, requestBody) {
// 'this' provides access to the request context
const contentType = this.req.headers["content-type"];
const userAgent = this.req.headers["user-agent"];
return [200, {
message: "User created",
metadata: {
contentType,
userAgent,
timestamp: Date.now()
}
}];
});Install with Tessl CLI
npx tessl i tessl/npm-nockdocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10