HttpRequestHandler
An HTTP request handler to declare responses for intercepted requests. When multiple handlers of the same interceptor
match the same method and path, the last handler created with
interceptor.<method>(path)
will be
used.
HttpRequestHandler
operationsIf you are using a remote interceptor, make sure to await
the handler before making requests. This is because many
remote handler operations are asynchronous and require communication with the
interceptor server.
If you don't await the handler, your declarations may not be committed to the server before the requests are made,
leading to non-deterministic behavior. A single await
is enough, even if you are making multiple declarations, as
HttpRequestHandler
automatically batches all changes.
// Instead of awaiting each operation separately:
const handler = interceptor.patch('/users/:userId');
await handler.with((request) => /^\d+$/.test(request.pathParams.userId));
await handler.with({ body: { username: 'new-username' } });
await handler.respond({ status: 204 });
await handler.times(1);
// You can await the handler once:
const handler = await interceptor
.patch('/users/:userId')
.with((request) => /^\d+$/.test(request.pathParams.userId))
.with({ body: { username: 'new-username' } })
.respond({ status: 204 })
.times(1);
When using a local interceptor, you can safely skip awaiting the handler, as all operations are synchronous and immediate.
handler.method
​
The method that matches this handler.
Type: HttpMethod
(readonly)
handler.path
​
The path that matches this handler. The base URL of the interceptor is not included, but it is used when matching requests.
Type: string
(readonly)
handler.with()
​
Declares a restriction to intercepted request matches. 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.
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. If you want to match only requests with the exact
values declared, use exact: true
.
- Local interceptor
- Remote interceptor
handler.with(restriction);
handler.with((request) => matchesRequest);
await handler.with(restriction);
await handler.with((request) => matchesRequest);
Arguments:
-
restriction:
HttpRequestHandlerRestriction<Schema, Method, Path>
The restriction to match intercepted requests. It can be static, defined as an object with the following properties:
-
headers:
HttpRequestHandlerHeadersStaticRestriction<Schema, Method, Path> | undefined
A set of headers that the intercepted request must contain to match the handler. If exact is
true
, the request must contain exactly these headers and no others. -
searchParams:
HttpRequestHandlerSearchParamsStaticRestriction<Schema, Method, Path> | undefined
A set of search params that the intercepted request must contain to match the handler. If exact is
true
, the request must contain exactly these search params and no others. -
body:
HttpRequestHandlerBodyStaticRestriction<Schema, Method, Path> | undefined
The body that the intercepted request must contain to match the handler. If exact is
true
, the request must contain exactly this body and no other. -
exact:
boolean | undefined
(default:false
)If
true
, the request must contain exactly the headers, search params, and body declared in this restriction. Otherwise, the request must contain at least them.
Restrictions can also be computed, declared as a function to dynamically decide how to check the request:
Arguments:
-
request:
HttpInterceptorRequest<Path, MethodSchema>
The intercepted request to check against the restriction.
Returns:
Promise<boolean> | boolean
A boolean indicating whether the intercepted request matches the restriction.
-
Returns: HttpRequestHandler<Method, Path, StatusCode>
The same handler, now considering the specified restriction.
If the interceptor is local, the restriction is immediately applied to the handler. If the interceptor is remote, the restriction is asynchronously sent to the interceptor server. Because of the asynchronous nature of remote interceptors, make sure to await the handler before making requests.
Related:
handler.respond()
​
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.
- Local interceptor
- Remote interceptor
handler.respond(declaration);
handler.respond((request) => declaration);
await handler.respond(declaration);
await handler.respond((request) => declaration);
Arguments:
-
declaration:
HttpRequestHandlerResponseDeclaration<MethodSchema, StatusCode> | HttpRequestHandlerResponseDeclarationFactory<MethodSchema, StatusCode>
The response declaration or a factory to create it. It can be a static object:
-
status:
HttpStatusCode
The status code for the response.
-
headers:
HttpHeadersInit<HeadersSchema> | undefined
The headers to include in the response.
-
body:
HttpBody
The body to include in the response.
A declaration can also be a function to dynamically decide how to respond to the request:
Arguments:
-
request:
HttpInterceptorRequest<Path, MethodSchema>
The intercepted request to respond to.
Returns:
Promise<HttpRequestHandlerResponseDeclaration<MethodSchema, StatusCode>> | HttpRequestHandlerResponseDeclaration<MethodSchema, StatusCode>
-
Returns: HttpRequestHandler<Method, Path, StatusCode>
The same handler, now including type information about the response declaration based on the status code.
If the interceptor is local, the response is immediately applied to the handler. If the interceptor is remote, the response is asynchronously sent to the interceptor server. Because of the asynchronous nature of remote interceptors, make sure to await the handler before making requests.
handler.times()
​
Declares a number of intercepted requests that the handler will be able to match and return its response.
If only one argument is provided, the handler will match exactly that number of requests. In case of two arguments, the handler will consider an inclusive range, matching at least the minimum (first argument) and at most the maximum (second argument) number of requests.
Once the handler receives more requests than the maximum number declared, it will stop matching requests and returning its response. In this case, Zimic will try other handlers until one eligible is found; otherwise the request will be either bypassed or rejected.
- Local interceptor
- Remote interceptor
handler.times(numberOfRequests);
handler.times(minNumberOfRequests, maxNumberOfRequests);
await handler.times(numberOfRequests);
await handler.times(minNumberOfRequests, maxNumberOfRequests);
Arguments:
-
numberOfRequests:
number
If only one argument is provided, this is the exact number of requests to match. If two arguments are provided, this is considered a minimum.
-
maxNumberOfRequests:
number | undefined
If two arguments are provided, this is the maximum number of requests to match.
Returns: HttpRequestHandler<Method, Path, StatusCode>
The same handler, now considering the specified number of requests.
To make sure that all expected requests were made, use
interceptor.checkTimes()
or
handler.checkTimes()
.
interceptor.checkTimes()
is generally
preferred, as it checks all handlers created by the interceptor with a single call.
Related:
handler.checkTimes()
​
Checks if the handler has matched the expected number of requests declared with handler.times()
.
If the handler has matched fewer or more requests than expected, this method will throw a TimesCheckError
error
pointing to the handler.times()
API reference that was not satisfied.
This is useful at the end of a test to ensure that all expected requests were made. If you are calling
interceptor.checkTimes()
after each test,
you can skip this call, as it will be called automatically for all handlers created by the interceptor.
When requestSaving.enabled
is true
in
your interceptor, the TimesCheckError
errors will also list each unmatched request with diff of the expected and
received data. This is useful for debugging requests that did not match a handler with
handler.with
restrictions.
- Local interceptor
- Remote interceptor
handler.checkTimes();
Returns: void
await handler.checkTimes();
Returns: Promise<void>
handler.clear()
​
Clears any response declared with handler.respond()
, restrictions declared with
handler.with()
, 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()
.
- Local interceptor
- Remote interceptor
handler.clear();
await handler.clear();
Returns: HttpRequestHandler<Method, Path, StatusCode>
The same handler, now cleared of any response, restrictions, and intercepted requests.
handler.requests
​
The intercepted requests that matched this handler, along with the responses returned to each of them. This is useful to allow additional validation in tests, if you are not using declarative assertions or you need more advanced checks.
Type: InterceptedHttpInterceptorRequest<Path, MethodSchema, StatusCode>[]
handler.requests
requires request saving to be enabledThis method can only be used if
requestSaving.enabled
is true
in the
interceptor.