-
Notifications
You must be signed in to change notification settings - Fork 2
api‐zimic‐interceptor‐http
-
HttpInterceptor
-
HttpRequestHandler
- Intercepted HTTP resources
This module exports resources to create HTTP interceptors and mock HTTP responses.
HTTP interceptors provide the main API to handle HTTP requests and return mock responses. The methods, paths, status codes, parameters, and responses are statically-typed based on the service schema.
Each interceptor represents a service and can be used to mock its paths and methods.
Creates an HTTP interceptor, the main interface to intercept HTTP requests and return responses. Learn more about declaring interceptor schemas.
Tip
If you are using TypeScript and have an OpenAPI v3 schema, you can use
zimic typegen
to automatically generate types for your interceptor schema!
A local interceptor is configured with type: 'local'
. The baseURL
represents the URL should be matched by this
interceptor. Any request starting with the baseURL
will be intercepted if a matching handler
exists.
import { httpInterceptor } from 'zimic/interceptor/http';
interface User {
username: string;
}
const interceptor = httpInterceptor.create<{
'/users/:id': {
GET: {
response: {
200: { body: User };
};
};
};
}>({
type: 'local',
baseURL: 'http://localhost:3000',
});
A remote interceptor is configured with type: 'remote'
. The baseURL
points to an
interceptor server. Any request starting with the baseURL
will be intercepted if a matching
handler exists.
import { httpInterceptor } from 'zimic/interceptor/http';
interface User {
username: string;
}
const interceptor = httpInterceptor.create<{
'/users/:id': {
GET: {
response: {
200: { body: User };
};
};
};
}>({
// The interceptor server is at http://localhost:4000
// `/my-service` is a path to differentiate from other
// interceptors using the same server
type: 'remote',
baseURL: 'http://localhost:4000/my-service',
});
A single interceptor server is perfectly capable of handling multiple interceptors and requests. Thus, additional paths are supported and might be necessary to differentiate between conflicting interceptors. If you may have multiple threads or processes applying mocks concurrently to the same interceptor server, it's important to keep the interceptor base URLs unique. Also, make sure that your application is considering the correct URL when making requests.
const interceptor = httpInterceptor.create<{
// ...
}>({
type: 'remote',
// Declaring a base URL with a unique identifier to prevent conflicts
baseURL: `http://localhost:4000/my-service-${crypto.randomUUID()}`,
});
// Your application should use this base URL when making requests
const baseURL = interceptor.baseURL();
When a request is not matched by any interceptor handlers, it is considered unhandled and will be logged to the console by default.
Tip
If you expected a request to be handled, but it was not, make sure that the interceptor base URL, path, method, and restrictions correctly match the request. Additionally, confirm that no errors occurred while creating the response.
In a local interceptor, unhandled requests can be either bypassed or rejected. Bypassed requests reach the real network, whereas rejected requests fail with an network error. The default behavior in local interceptors is to bypass unhandled requests.
On the other hand, remote interceptors and interceptor server always reject unhandled requests. This is because the unhandled requests have already reached the interceptor server, so there would be no way of bypassing them at this point.
You can override the default logging behavior per interceptor with onUnhandledRequest
in
httpInterceptor.create(options)
. onUnhandledRequest
also accepts a function to
dynamically determine which strategy to use for an unhandled request.
Example 1: Ignore all unhandled requests with no logging:
import { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
onUnhandledRequest: {
action: 'bypass', // Allow unhandled requests to reach the real network
log: false, // Do not log warnings about unhandled requests
},
});
Example 2: Reject all unhandled requests with logging:
import { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
onUnhandledRequest: {
action: 'reject', // Do not allow unhandled requests to reach the real network
log: true, // Log warnings about unhandled requests
},
});
Example 3: Dynamically ignore or reject unhandled requests:
import { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
onUnhandledRequest: async (request) => {
const url = new URL(request.url);
// Ignore only unhandled requests to /assets
if (url.pathname.startsWith('/assets')) {
return { action: 'bypass', log: false };
}
// Reject all other unhandled requests
return { action: 'reject', log: true };
},
});
If you want to override the default logging behavior for all interceptors, or requests that did not match any known base
URL, you can use httpInterceptor.default.local.onUnhandledRequest
or
httpInterceptor.default.remote.onUnhandledRequest
. Keep in mind that defining an onUnhandledRequest
when creating an
interceptor will take precedence over httpInterceptor.default.local.onUnhandledRequest
and
httpInterceptor.default.remote.onUnhandledRequest
.
Example 4: Ignore all unhandled requests with no logging:
import { httpInterceptor } from 'zimic/interceptor/http';
httpInterceptor.default.local.onUnhandledRequest = {
action: 'bypass',
log: false,
};
httpInterceptor.default.remote.onUnhandledRequest = {
action: 'reject',
log: false,
};
Example 5: Reject all unhandled requests with logging:
import { httpInterceptor } from 'zimic/interceptor/http';
// For local interceptors:
httpInterceptor.default.local.onUnhandledRequest = {
action: 'reject',
log: true,
};
// For remote interceptors:
httpInterceptor.default.remote.onUnhandledRequest = {
action: 'reject',
log: true,
};
Example 6: Dynamically ignore or reject all unhandled requests:
import { httpInterceptor } from 'zimic/interceptor/http';
// For local interceptors:
httpInterceptor.default.local.onUnhandledRequest = (request) => {
const url = new URL(request.url);
// Ignore only unhandled requests to /assets
if (url.pathname.startsWith('/assets')) {
return { action: 'bypass', log: false };
}
// Reject all other unhandled requests
return { action: 'reject', log: true };
};
// For remote interceptors:
httpInterceptor.default.remote.onUnhandledRequest = (request) => {
const url = new URL(request.url);
return {
action: 'reject', // Reject all unhandled requests
log: !url.pathname.startsWith('/assets'), // Log warnings for all unhandled requests except /assets
};
};
The option saveRequests
indicates whether request handlers should save their intercepted
requests in memory and make them accessible through handler.requests()
.
This setting is configured per interceptor and is false
by default. If set to true
, each handler will keep track of
their intercepted requests in memory.
Important
Saving the intercepted requests will lead to a memory leak if not accompanied by clearing of the interceptor or disposal of the handlers (i.e. garbage collection).
If you plan on accessing those requests, such as to assert them in your tests, set saveRequests
to true
and make
sure to regularly clear the interceptor. A common practice is to call interceptor.clear()
after each test.
See Testing for an example of how to manage the lifecycle of interceptors in your tests.
import { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
saveRequests: true,
});
Tip
If you use an interceptor both in tests and as a standalone mock server, consider setting saveRequests
based on an
environment variable. This allows you to access the requests in tests, while preventing memory leaks in long-running
mock servers.
import { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
saveRequests: process.env.NODE_ENV === 'test',
});
Starts the interceptor. Only interceptors that are running will intercept requests.
await interceptor.start();
When targeting a browser environment with a local interceptor, make sure to follow the client-side post-install guide before starting your interceptors.
Stops the interceptor. Stopping an interceptor will also clear its registered handlers and responses.
await interceptor.stop();
Returns whether the interceptor is currently running and ready to use.
const isRunning = interceptor.isRunning();
Returns the base URL of the interceptor.
const baseURL = interceptor.baseURL();
Returns the platform used by the interceptor (browser
or node
).
const platform = interceptor.platform();
Creates an HttpRequestHandler
for the given method and path. The path and method must be
declared in the interceptor schema.
The supported methods are: get
, post
, put
, patch
, delete
, head
, and options
.
When using a remote interceptor, creating a handler is an asynchronous
operation, so you need to await
it. You can also chain any number of operations and apply them by awaiting the
handler.
Using a local interceptorimport { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<{
'/users': {
GET: {
response: {
200: { body: User[] };
};
};
};
}>({
type: 'local',
baseURL: 'http://localhost:3000',
});
const listHandler = interceptor.get('/users').respond({
status: 200
body: [{ username: 'diego-aquino' }],
}); |
Using a remote interceptorimport { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<{
'/users': {
GET: {
response: {
200: { body: User[] };
};
};
};
}>({
type: 'remote',
baseURL: 'http://localhost:4000/my-service',
});
const listHandler = await interceptor.get('/users').respond({
status: 200
body: [{ username: 'diego-aquino' }],
}); |
Paths with parameters are supported, such as /users/:id
. Even when using a computed path (e.g. /users/1
), the
original path is automatically inferred, guaranteeing type safety.
import { httpInterceptor } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<{
'/users/:id': {
PUT: {
request: {
body: { username: string };
};
response: {
204: {};
};
};
};
}>({
type: 'local',
baseURL: 'http://localhost:3000',
});
interceptor.get('/users/:id'); // Matches any id
interceptor.get(`/users/${1}`); // Only matches id 1
request.pathParams
contains the parsed path parameters of a request and have their type automatically inferred from
the path string. For example, the path /users/:userId
will result in a request.pathParams
of type
{ userId: string }
.
Using a local interceptorconst updateHandler = interceptor.put('/users/:id').respond((request) => {
console.log(request.pathParams); // { id: '1' }
return {
status: 200,
body: { username: 'diego-aquino' },
};
});
await fetch('http://localhost:3000/users/1', { method: 'PUT' }); |
Using a remote interceptorconst updateHandler = await interceptor.put('/users/:id').respond((request) => {
console.log(request.pathParams); // { id: '1' }
return {
status: 200,
body: { username: 'diego-aquino' },
};
});
await fetch('http://localhost:3000/users/1', { method: 'PUT' }); |
Clears all of the HttpRequestHandler
instances created by this interceptor, including their
registered responses and intercepted requests. After calling this method, the interceptor will no longer intercept any
requests until new mock responses are registered.
This method is useful to reset the interceptor mocks between tests.
Using a local interceptorinterceptor.clear(); |
Using a remote interceptorawait interceptor.clear(); |
Infers the schema of an HTTP interceptor.
import { httpInterceptor, type InferHttpInterceptorSchema } from 'zimic/interceptor/http';
const interceptor = httpInterceptor.create<{
'/users': {
GET: {
response: { 200: { body: User[] } };
};
};
}>({
type: 'local',
baseURL: 'http://localhost:3000',
});
type Schema = InferHttpInterceptorSchema<typeof interceptor>;
// {
// '/users': {
// GET: {
// response: { 200: { body: User[] } };
// };
// };
// }
HTTP request handlers allow declaring HTTP responses to return for intercepted requests. They also keep track of the intercepted requests and their responses, which can be used to check if the requests your application has made are correct.
When multiple handlers match the same method and path, the last created with
interceptor.<method>(path)
will be used.
Returns the method that matches a handler.
Using a local interceptorconst creationHandler = interceptor.post('/users');
const method = creationHandler.method();
console.log(method); // 'POST' |
Using a remote interceptorconst handler = await interceptor.post('/users');
const method = handler.method();
console.log(method); // 'POST' |
Returns the path that matches a handler. The base URL of the interceptor is not included, but it is used when matching requests.
Using a local interceptorconst listHandler = interceptor.get('/users');
const path = listHandler.path();
console.log(path); // '/users' |
Using a remote interceptorconst handler = await interceptor.get('/users');
const path = handler.path();
console.log(path); // '/users' |
Declares a restriction to intercepted requests. headers
, searchParams
, and body
are supported to limit which
requests will match the handler and receive the mock response. If multiple restrictions are declared, either in a single
object or with multiple calls to handler.with()
, all of them must be met, essentially creating an AND condition.
Declaring restrictions for headers:
Using a local interceptorconst creationHandler = interceptor
.get('/users')
.with({
headers: { authorization: `Bearer ${token}` },
})
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Using a remote interceptorconst creationHandler = await interceptor
.get('/users')
.with({
headers: { authorization: `Bearer ${token}` },
})
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
An equivalent alternative using HttpHeaders
:
Using a local interceptorimport { type HttpSchema, HttpHeaders } from 'zimic/http';
type UserListHeaders = HttpSchema.Headers<{
authorization: string;
}>;
const headers = new HttpHeaders<UserListHeaders>({
authorization: `Bearer ${token}`,
});
const creationHandler = interceptor
.get('/users')
.with({ headers })
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Using a remote interceptorimport { type HttpSchema, HttpHeaders } from 'zimic/http';
type UserListHeaders = HttpSchema.Headers<{
authorization: string;
}>;
const headers = new HttpHeaders<UserListHeaders>({
authorization: `Bearer ${token}`,
});
const creationHandler = await interceptor
.get('/users')
.with({ headers })
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Declaring restrictions for search params:
Using a local interceptorconst creationHandler = interceptor
.get('/users')
.with({
searchParams: { username: 'diego-aquino' },
})
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Using a remote interceptorconst creationHandler = await interceptor
.get('/users')
.with({
searchParams: { username: 'diego-aquino' },
})
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
An equivalent alternative using HttpSearchParams
:
Using a local interceptorimport { type HttpSchema, HttpSearchParams } from 'zimic/http';
type UserListSearchParams = HttpSchema.SearchParams<{
username?: string;
}>;
const searchParams = new HttpSearchParams<UserListSearchParams>({
username: 'diego-aquino',
});
const creationHandler = interceptor
.get('/users')
.with({ searchParams })
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Using a remote interceptorimport { type HttpSchema, HttpSearchParams } from 'zimic/http';
type UserListSearchParams = HttpSchema.SearchParams<{
username?: string;
}>;
const searchParams = new HttpSearchParams<UserListSearchParams>({
username: 'diego-aquino',
});
const creationHandler = await interceptor
.get('/users')
.with({ searchParams })
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Declaring restrictions for a JSON body:
Using a local interceptorconst creationHandler = interceptor
.post('/users')
.with({
body: { username: 'diego-aquino' },
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
Using a remote interceptorconst creationHandler = await interceptor
.post('/users')
.with({
body: { username: 'diego-aquino' },
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
For JSON bodies to be correctly parsed, make sure that the intercepted requests have the header
content-type: application/json
.
Declaring restrictions for a form data body:
Using a local interceptorimport { type HttpSchema, HttpFormData } from 'zimic/http';
type UserCreationData = HttpSchema.FormData<{
username: string;
profilePicture: Blob;
}>;
const formData = new HttpFormData<UserCreationData>();
formData.append('username', 'diego-aquino');
formData.append(
'profilePicture',
new File(['content'], 'profile.png', {
type: 'image/png',
}),
);
const creationHandler = interceptor
.post('/users')
.with({
body: formData,
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
Using a remote interceptorimport { type HttpSchema, HttpFormData } from 'zimic/http';
type UserCreationData = HttpSchema.FormData<{
username: string;
profilePicture: Blob;
}>;
const formData = new HttpFormData<UserCreationData>();
formData.append('username', 'diego-aquino');
formData.append(
'profilePicture',
new File(['content'], 'profile.png', {
type: 'image/png',
}),
);
const creationHandler = await interceptor
.post('/users')
.with({
body: formData,
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
For form data bodies to be correctly parsed, make sure that the intercepted requests have the header
content-type: multipart/form-data
.
Declaring restrictions for a blob body:
Using a local interceptorconst creationHandler = interceptor
.post('/users')
.with({
body: new Blob(['content'], {
type: 'application/octet-stream',
}),
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
Using a remote interceptorconst creationHandler = await interceptor
.post('/users')
.with({
body: new Blob(['content'], {
type: 'application/octet-stream',
}),
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
For blob bodies to be correctly parsed, make sure that the intercepted requests have the header content-type
indicating a binary data, such as application/octet-stream
, image/png
, audio/mp3
, etc.
Declaring restrictions for a plain text body:
Using a local interceptorconst creationHandler = interceptor
.post('/users')
.with({
body: 'content',
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
Using a remote interceptorconst creationHandler = await interceptor
.post('/users')
.with({
body: 'content',
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
Declaring restrictions for search params (x-www-form-urlencoded
) body:
Using a local interceptorimport { type HttpSchema, HttpSearchParams } from 'zimic/http';
type UserGetByIdSearchParams = HttpSchema.SearchParams<{
username: string;
}>;
const searchParams = new HttpSearchParams<UserGetByIdSearchParams>({
username: 'diego-aquino',
});
const creationHandler = interceptor
.post('/users')
.with({
body: searchParams,
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
Using a remote interceptorimport { type HttpSchema, HttpSearchParams } from 'zimic/http';
type UserGetByIdSearchParams = HttpSchema.SearchParams<{
username: string;
}>;
const searchParams = new HttpSearchParams<UserGetByIdSearchParams>({
username: 'diego-aquino',
});
const creationHandler = await interceptor
.post('/users')
.with({
body: searchParams,
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
For plain text bodies to be correctly parsed, make sure that the intercepted requests have the header content-type
indicating a plain text, such as text/plain
.
By default, restrictions use exact: false
, meaning that any request containing the declared restrictions will
match the handler, regardless of having more properties or values. In the examples above, requests with more properties
in the headers, search params, or body would still match the restrictions.
If you want to match only requests with the exact values declared, you can use exact: true
:
Using a local interceptorconst creationHandler = interceptor
.post('/users')
.with({
headers: { 'content-type': 'application/json' },
body: { username: 'diego-aquino' },
exact: true, // Only requests with these exact headers and body will match
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
Using a remote interceptorconst creationHandler = await interceptor
.post('/users')
.with({
headers: { 'content-type': 'application/json' },
body: { username: 'diego-aquino' },
exact: true, // Only requests with these exact headers and body will match
})
.respond({
status: 201,
body: { username: 'diego-aquino' },
}); |
A function is also supported to declare restrictions in case they are dynamic. Learn more about the request
object at
Intercepted HTTP resources.
Using a local interceptorconst creationHandler = interceptor
.post('/users')
.with((request) => {
const accept = request.headers.get('accept');
return accept !== null && accept.startsWith('application');
})
.respond({
status: 201,
body: [{ username: 'diego-aquino' }],
}); |
Using a remote interceptorconst creationHandler = await interceptor
.post('/users')
.with((request) => {
const accept = request.headers.get('accept');
return accept !== null && accept.startsWith('application');
})
.respond({
status: 201,
body: [{ username: 'diego-aquino' }],
}); |
The function should return a boolean: true
if the request matches the handler and should receive the mock response;
false
otherwise.
Declares a response to return for matched intercepted requests.
When the handler matches a request, it will respond with the given declaration. The response type is statically validated against the schema of the interceptor.
Declaring responses with JSON body:
Using a local interceptorconst listHandler = interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Using a remote interceptorconst listHandler = await interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
}); |
Declaring responses with form data body:
Using a local interceptorimport { type HttpSchema, HttpFormData } from 'zimic/http';
type UserGetByIdData = HttpSchema.FormData<{
username: string;
profilePicture: Blob;
}>;
const formData = new HttpFormData<UserGetByIdData>();
formData.append('username', 'diego-aquino');
formData.append(
'profilePicture',
new File(['content'], 'profile.png', {
type: 'image/png',
}),
);
const listHandler = interceptor.get('/users/:id').respond({
status: 200,
body: formData,
}); |
Using a remote interceptorimport { type HttpSchema, HttpFormData } from 'zimic/http';
type UserGetByIdData = HttpSchema.FormData<{
username: string;
profilePicture: Blob;
}>;
const formData = new HttpFormData<UserGetByIdData>();
formData.append('username', 'diego-aquino');
formData.append(
'profilePicture',
new File(['content'], 'profile.png', {
type: 'image/png',
}),
);
const listHandler = await interceptor.get('/users/:id').respond({
status: 200,
body: formData,
}); |
Declaring responses with blob body:
Using a local interceptorconst listHandler = interceptor.get('/users').respond({
status: 200,
body: new Blob(['content'], {
type: 'application/octet-stream',
}),
}); |
Using a remote interceptorconst listHandler = await interceptor.get('/users').respond({
status: 200,
body: new Blob(['content'], {
type: 'application/octet-stream',
}),
}); |
Declaring responses with plain text body:
Using a local interceptorconst listHandler = interceptor.get('/users').respond({
status: 200,
body: 'content',
}); |
Using a remote interceptorconst listHandler = await interceptor.get('/users').respond({
status: 200,
body: 'content',
}); |
Declaring responses with search params (x-www-form-urlencoded
) body:
Using a local interceptorimport { type HttpSchema, HttpSearchParams } from 'zimic/http';
type UserGetByIdSearchParams = HttpSchema.SearchParams<{
username: string;
}>;
const searchParams = new HttpSearchParams<UserGetByIdSearchParams>({
username: 'diego-aquino',
});
const listHandler = interceptor.get('/users').respond({
status: 200,
body: searchParams,
}); |
Using a remote interceptorimport { type HttpSchema, HttpSearchParams } from 'zimic/http';
type UserGetByIdSearchParams = HttpSchema.SearchParams<{
username: string;
}>;
const searchParams = new HttpSearchParams<UserGetByIdSearchParams>({
username: 'diego-aquino',
});
const listHandler = await interceptor.get('/users').respond({
status: 200,
body: searchParams,
}); |
A function is also supported to declare a response in case it is dynamic. Learn more about the request
object at
Intercepted HTTP resources.
Using a local interceptorconst listHandler = interceptor.get('/users').respond((request) => {
const username = request.searchParams.get('username');
if (!username) {
return { status: 400 };
}
return {
status: 200,
body: [{ username }],
};
}); |
Using a remote interceptorconst listHandler = await interceptor.get('/users').respond((request) => {
const username = request.searchParams.get('username');
if (!username) {
return { status: 400 };
}
return {
status: 200,
body: [{ username }],
};
}); |
Clears any response declared with handler.respond(declaration)
, making the handler
stop matching requests. The next handler, created before this one, that matches the same method and path will be used if
present. If not, the requests of the method and path will not be intercepted.
To make the handler match requests again, register a new response with
handler.respond(declaration)
.
This method is useful to skip a handler. It is more gentle than handler.clear()
, as it only
removed the response, keeping restrictions and intercepted requests.
Using a local interceptorconst listHandler = interceptor.get('/users').respond({
status: 200,
body: [],
});
const otherListHandler = interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
otherListHandler.bypass();
// Now, requests GET /users will match `listHandler` and receive an empty array |
Using a remote interceptorconst listHandler = await interceptor.get('/users').respond({
status: 200,
body: [],
});
const otherListHandler = await interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
await otherListHandler.bypass();
// Now, requests GET /users will match `listHandler` and receive an empty array |
Clears any response declared with handler.respond(declaration)
, restrictions
declared with handler.with(restriction)
, and intercepted requests, making the handler
stop matching requests. The next handler, created before this one, that matches the same method and path will be used if
present. If not, the requests of the method and path will not be intercepted.
To make the handler match requests again, register a new response with handler.respond()
.
This method is useful to reset handlers to a clean state between tests. It is more aggressive than
handler.bypass()
, as it also clears restrictions and intercepted requests.
Using a local interceptorconst listHandler = interceptor.get('/users').respond({
status: 200,
body: [],
});
const otherListHandler = interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
otherListHandler.clear();
// Now, requests GET /users will match `listHandler` and receive an empty array
otherListHandler.requests(); // Now empty |
Using a remote interceptorconst listHandler = await interceptor.get('/users').respond({
status: 200,
body: [],
});
const otherListHandler = await interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
await otherListHandler.clear();
// Now, requests GET /users will match `listHandler` and receive an empty array
await otherListHandler.requests(); // Now empty |
Returns the intercepted requests that matched this handler, along with the responses returned to each of them. This is
useful for testing that the correct requests were made by your application. Learn more about the request
and
response
objects at Intercepted HTTP resources.
Important
This method can only be used if saveRequests
was set to true
when creating the interceptor. See
Saving intercepted requests for more information.
Using a local interceptorconst updateHandler = interceptor.put('/users/:id').respond((request) => {
const newUsername = request.body.username;
return {
status: 200,
body: [{ username: newUsername }],
};
});
await fetch(`http://localhost:3000/users/${1}`, {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ username: 'new' }),
});
const updateRequests = await updateHandler.requests();
expect(updateRequests).toHaveLength(1);
expect(updateRequests[0].pathParams).toEqual({ id: '1' });
expect(updateRequests[0].body).toEqual({ username: 'new' });
expect(updateRequests[0].response.status).toBe(200);
expect(updateRequests[0].response.body).toEqual([{ username: 'new' }]); |
Using a remote interceptorconst updateHandler = await interceptor.put('/users/:id').respond((request) => {
const newUsername = request.body.username;
return {
status: 200,
body: [{ username: newUsername }],
};
});
await fetch(`http://localhost:3000/users/${1}`, {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ username: 'new' }),
});
const updateRequests = await updateHandler.requests();
expect(updateRequests).toHaveLength(1);
expect(updateRequests[0].pathParams).toEqual({ id: '1' });
expect(updateRequests[0].body).toEqual({ username: 'new' });
expect(updateRequests[0].response.status).toBe(200);
expect(updateRequests[0].response.body).toEqual([{ username: 'new' }]); |
The intercepted requests and responses are typed based on their
interceptor schema. They are available as simplified objects based on the
Request
and
Response
web APIs. body
contains the parsed body, while typed
headers, path params and search params are in headers
, pathParams
, and searchParams
, respectively.
The body is automatically parsed based on the header content-type
of the request or response. The following table
shows how each type is parsed, where *
indicates any other resource that does not match the previous types:
content-type |
Parsed to |
---|---|
application/json |
JSON |
application/xml |
String |
application/x-www-form-urlencoded |
HttpSearchParams |
application/* (others) |
Blob |
multipart/form-data |
HttpFormData |
multipart/* (others) |
Blob |
text/* |
String |
image/* |
Blob |
audio/* |
Blob |
font/* |
Blob |
video/* |
Blob |
*/* (others) |
JSON if possible, otherwise String
|
If no content-type
exists or it is unknown, Zimic tries to parse the body as JSON and falls back to plain text if it
fails.
If you need access to the original Request
and Response
objects, you can use the request.raw
property:
console.log(request.raw); // Request{}
console.log(request.response.raw); // Response{}
© Zimic
npm |
Docs | Issues |
Examples | Roadmap
Help us improve these docs!
Report an issue or
edit on GitHub.