Zimic
TypeScript-first HTTP request mocking
[!NOTE]
🚧 This project is still experimental and under active development!
Check our roadmap to v1. Contributors and ideas are welcome!
Zimic is a lightweight, TypeScript-first HTTP request mocking library, inspired by Zod’s type inference and using MSW under the hood.
Features
Zimic provides a simple, flexible and type-safe way to mock HTTP requests.
- ⚡ Statically-typed mocks. Declare your HTTP endpoints and get full type inference and validation when applying mocks.
- 🔗 Network-level intercepts. Internally, Zimic uses MSW, which intercepts HTTP requests right before they leave your app. This means that no parts of your code are stubbed or skipped. From you application’s point of view, the mocked requests are indistinguishable from the real ones.
- 🔧 Flexibility. You can simulate real application workflows by mocking each necessary endpoints. This is specially useful in testing, making sure the real path your application takes is covered and the sent requests are correct.
- 💡 Simplicity. Zimic was designed from scratch to encourage clarity, simplicity and standardization in your mocks. Check our Getting started guide and starting mocking!
Table of contents
- Features
- Table of contents
- Getting started
- Usage
zimic
APIzimic/interceptor
API- CLI
- Changelog
- Development
Getting started
1. Requirements
TypeScript >=4.7
strict
mode enabled in yourtsconfig.json
:{ // ... "compilerOptions": { // ... "strict": true, }, }
npm
2. Install from Manager | Command |
---|---|
npm | npm install zimic --save-dev |
yarn | yarn add zimic --dev |
pnpm | pnpm add zimic --dev |
bun | bun add zimic --dev |
Canary versions are released under the canary
tag:
Manager | Command |
---|---|
npm | npm install zimic@canary --save-dev |
yarn | yarn add zimic@canary --dev |
pnpm | pnpm add zimic@canary --dev |
bun | bun add zimic@canary --dev |
3. Post-install
Node.js post-install
No additional configuration is necessary for Node.js. Check out the usage guide and start mocking!
Browser post-install
To use Zimic on a browser, you should first initialize the mock service worker in your public directory:
npx zimic browser init <publicDirectory>
This will create a mockServiceWorker.js
file in the provided public directory, which is necessary to intercept
requests and mock responses. We recommend not deploying this file to production.
Usage
Examples
Visit our examples to see how to use Zimic with popular frameworks and libraries!
Basic usage
To start using Zimic, create a worker targeting your platform.
import { createHttpInterceptorWorker } from 'zimic/interceptor'; const worker = createHttpInterceptorWorker({ platform: 'node', // Or 'browser' });
Then, create your first interceptor:
import { JSONValue } from 'zimic'; import { createHttpInterceptor } from 'zimic/interceptor'; type User = JSONValue<{ username: string; }>; const interceptor = createHttpInterceptor<{ '/users': { GET: { response: { 200: { body: User[] }; }; }; }; }>({ worker, baseURL: 'http://localhost:3000', });
In this example, we’re creating an interceptor for a service with a single path,
/users
, that supports aGET
method. The response for a successful request is an array ofUser
objects, which is checked to be a valid JSON usingJSONValue
. Learn more at Declaring service schemas.Finally, start the worker to intercept requests:
await worker.start();
Now, you can start intercepting requests and returning mock responses!
const listHandler = interceptor.get('/users').respond({ status: 200, body: [{ username: 'diego-aquino' }], }); const response = await fetch('http://localhost:3000/users'); const users = await response.json(); console.log(users); // [{ username: 'diego-aquino' }]
More usage examples and recommendations are available at zimic/interceptor
API and
Examples.
Testing
We recommend managing the lifecycle of your workers and interceptors using beforeAll
and afterAll
hooks in your test
setup file. An example using Jest/Vitest structure:
Create a worker:
tests/interceptors/worker.ts
import { createHttpInterceptorWorker } from 'zimic/interceptor'; const worker = createHttpInterceptorWorker({ platform: 'node', // Or 'browser' }); export default worker;
Create interceptors for your services:
tests/interceptors/userInterceptor.ts
import { createHttpInterceptor } from 'zimic/interceptor'; import worker from './worker'; const userInterceptor = createHttpInterceptor<{ // User service schema }>({ worker, baseURL: 'http://localhost:3000', }); export default userInterceptor;
tests/interceptors/analyticsInterceptor.ts
import { createHttpInterceptor } from 'zimic/interceptor'; import worker from './worker'; const analyticsInterceptor = createHttpInterceptor<{ // Analytics service schema }>({ worker, baseURL: 'http://localhost:3001', }); export default analyticsInterceptor;
Create a setup file to manage lifecycle of the worker and the interceptors:
tests/setup.ts
import userInterceptor from './interceptors/userInterceptor'; import analyticsInterceptor from './interceptors/analyticsInterceptor'; beforeAll(async () => { // Start intercepting requests await worker.start(); }); beforeEach(async () => { // Clear all interceptors to make sure no tests affect each other userInterceptor.clear(); analyticsInterceptor.clear(); }); afterAll(async () => { // Stop intercepting requests await worker.stop(); });
zimic
API
This module provides general resources, such as HTTP classes and types.
[!TIP]
All APIs are documented using JSDoc, so you can view detailed descriptions directly in your IDE.
HttpHeaders
A superset of the built-in Headers
class, with a strictly-typed
schema. HttpHeaders
is fully compatible with Headers
and is used by Zimic to provide type safety when applying
mocks.
import { HttpHeaders } from 'zimic';
const headers = new HttpHeaders<{
accept?: string;
'content-type'?: string;
}>({
accept: '*/*',
'content-type': 'application/json',
});
const contentType = headers.get('content-type');
console.log(contentType); // 'application/json'
HttpHeaders
Comparing HttpHeaders
also provide the utility methods .equals
and .contains
, useful in comparisons with other headers:
import { HttpSchema, HttpHeaders } from 'zimic';
type HeaderSchema = HttpSchema.Headers<{
accept?: string;
'content-type'?: string;
}>;
const headers1 = new HttpHeaders<HeaderSchema>({
accept: '*/*',
'content-type': 'application/json',
});
const headers2 = new HttpHeaders<HeaderSchema>({
accept: '*/*',
'content-type': 'application/json',
});
const headers3 = new HttpHeaders<
HeaderSchema & {
'x-custom-header'?: string;
}
>({
accept: '*/*',
'content-type': 'application/json',
'x-custom-header': 'value',
});
console.log(headers1.equals(headers2)); // true
console.log(headers1.equals(headers3)); // false
console.log(headers1.contains(headers2)); // true
console.log(headers1.contains(headers3)); // false
console.log(headers3.contains(headers1)); // true
HttpSearchParams
A superset of the built-in URLSearchParams
class, with a
strictly-typed schema. HttpSearchParams
is fully compatible with URLSearchParams
and is used by Zimic to provide
type safety when applying mocks.
import { HttpSearchParams } from 'zimic';
const searchParams = new HttpSearchParams<{
names?: string[];
page?: `${number}`;
}>({
names: ['user 1', 'user 2'],
page: '1',
});
const names = searchParams.getAll('names');
console.log(names); // ['user 1', 'user 2']
const page = searchParams.get('page');
console.log(page); // '1'
HttpSearchParams
Comparing HttpSearchParams
also provide the utility methods .equals
and .contains
, useful in comparisons with other search
params:
import { HttpSchema, HttpSearchParams } from 'zimic';
type SearchParamsSchema = HttpSchema.SearchParams<{
names?: string[];
page?: `${number}`;
}>;
const searchParams1 = new HttpSearchParams<SearchParamsSchema>({
names: ['user 1', 'user 2'],
page: '1',
});
const searchParams2 = new HttpSearchParams<SearchParamsSchema>({
names: ['user 1', 'user 2'],
page: '1',
});
const searchParams3 = new HttpSearchParams<
SearchParamsSchema & {
orderBy?: `${'name' | 'email'}.${'asc' | 'desc'}[]`;
}
>({
names: ['user 1', 'user 2'],
page: '1',
orderBy: ['name.asc'],
});
console.log(searchParams1.equals(searchParams2)); // true
console.log(searchParams1.equals(searchParams3)); // false
console.log(searchParams1.contains(searchParams2)); // true
console.log(searchParams1.contains(searchParams3)); // false
console.log(searchParams3.contains(searchParams1)); // true
zimic/interceptor
API
This module provides a set of resources to create HTTP interceptors for both Node.js and browser environments.
[!TIP]
All APIs are documented using JSDoc, so you can view detailed descriptions directly in your IDE.
HttpInterceptorWorker
Workers are used by HttpInterceptor
’s to intercept HTTP requests and return mock responses. To
start intercepting requests, the worker must be started.
In a project, all interceptors can share the same worker.
createHttpInterceptorWorker
Creates an HTTP interceptor worker. A platform is required to specify the environment where the worker will be running:
Node.js:
import { createHttpInterceptorWorker } from 'zimic/interceptor'; const worker = createHttpInterceptorWorker({ platform: 'node' });
Browser:
import { createHttpInterceptorWorker } from 'zimic/interceptor'; const worker = createHttpInterceptorWorker({ platform: 'browser' });
worker.platform()
Returns the platform used by the worker (browser
or node
).
const platform = worker.platform();
worker.start()
Starts the worker, allowing it to be used by interceptors.
await worker.start();
When targeting a browser environment, make sure to run npx zimic browser init <publicDirectory>
on your terminal
before starting the worker. This initializes the mock service worker in your public directory. Learn more at
zimic browser init <publicDirectory
.
worker.stop()
Stops the worker, preventing it from being used by interceptors.
await worker.stop();
worker.isRunning()
Returns whether the worker is currently running and ready to use.
const isRunning = worker.isRunning();
HttpInterceptor
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. To intercept HTTP requests, an
interceptor needs a running HttpInterceptorWorker
.
Each interceptor represents a service and can be used to mock its paths and methods.
createHttpInterceptor
Creates an HTTP interceptor, the main interface to intercept HTTP requests and return responses. Learn more at Declaring service schemas.
import { JSONValue } from 'zimic';
import { createHttpInterceptorWorker, createHttpInterceptor } from 'zimic/interceptor';
const worker = createHttpInterceptorWorker({ platform: 'node' });
type User = JSONValue<{
username: string;
}>;
const interceptor = createHttpInterceptor<{
'/users/:id': {
GET: {
response: {
200: { body: User };
};
};
};
}>({
worker,
baseURL: 'http://localhost:3000',
});
Declaring service schemas
HTTP service schemas define the structure of the real services being used. This includes paths, methods, request and response bodies, and status codes. Based on the schema, interceptors will provide type validation when applying mocks.
An example of a complete interceptor schema:
import { HttpSchema, JSONValue } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
// Declaring base types
type User = JSONValue<{
username: string;
}>;
type UserCreationBody = JSONValue<{
username: string;
}>;
type NotFoundError = JSONValue<{
message: string;
}>;
type UserListSearchParams = HttpSchema.SearchParams<{
name?: string;
orderBy?: `${'name' | 'email'}.${'asc' | 'desc'}`[];
}>;
// Creating the interceptor
const interceptor = createHttpInterceptor<{
'/users': {
POST: {
request: {
headers: { accept: string };
body: UserCreationBody;
};
response: {
201: {
headers: { 'content-type': string };
body: User;
};
};
};
GET: {
request: {
searchParams: UserListSearchParams;
};
response: {
200: { body: User[] };
404: { body: NotFoundError };
};
};
};
'/users/:id': {
GET: {
response: {
200: { body: User };
404: { body: NotFoundError };
};
};
};
}>({
worker,
baseURL: 'http://localhost:3000',
});
Alternatively, you can compose the schema using utility types:
import { HttpSchema, JSONValue } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
// Declaring the base types
type User = JSONValue<{
username: string;
}>;
type UserCreationBody = JSONValue<{
username: string;
}>;
type NotFoundError = JSONValue<{
message: string;
}>;
type UserListSearchParams = HttpSchema.SearchParams<{
name?: string;
orderBy?: `${'name' | 'email'}.${'asc' | 'desc'}`[];
}>;
// Declaring user methods
type UserMethods = HttpSchema.Methods<{
POST: {
request: {
headers: { accept: string };
body: UserCreationBody;
};
response: {
201: {
headers: { 'content-type': string };
body: User;
};
};
};
GET: {
request: {
searchParams: UserListSearchParams;
};
response: {
200: { body: User[] };
404: { body: NotFoundError };
};
};
}>;
type UserGetByIdMethods = HttpSchema.Methods<{
GET: {
response: {
200: { body: User };
404: { body: NotFoundError };
};
};
}>;
// Declaring user paths
type UserPaths = HttpSchema.Paths<{
'/users': UserMethods;
}>;
type UserByIdPaths = HttpSchema.Paths<{
'/users/:id': UserGetByIdMethods;
}>;
// Declaring interceptor schema
type ServiceSchema = UserPaths & UserByIdPaths;
// Creating the interceptor
const interceptor = createHttpInterceptor<ServiceSchema>({
worker,
baseURL: 'http://localhost:3000',
});
Declaring paths
At the root level, each key represents a path or route of the service:
import { createHttpInterceptor } from 'zimic/interceptor';
const interceptor = createHttpInterceptor<{
'/users': {
// Path schema
};
'/users/:id': {
// Path schema
};
'/posts': {
// Path schema
};
}>({
worker,
baseURL: 'http://localhost:3000',
});
Alternatively, you can also compose paths using the utility type HttpSchema.Paths
:
import { HttpSchema } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
type UserPaths = HttpSchema.Paths<{
'/users': {
// Path schema
};
'/users/:id': {
// Path schema
};
}>;
type PostPaths = HttpSchema.Paths<{
'/posts': {
// Path schema
};
}>;
const interceptor = createHttpInterceptor<UserPaths & PostPaths>({
worker,
baseURL: 'http://localhost:3000',
});
Declaring methods
Each path can have one or more methods, (GET
, POST
, PUT
, PATCH
, DELETE
, HEAD
, and OPTIONS
). The method
names are case-sensitive.
import { createHttpInterceptor } from 'zimic/interceptor';
const interceptor = createHttpInterceptor<{
'/users': {
GET: {
// Method schema
};
POST: {
// Method schema
};
};
// Other paths
}>({
worker,
baseURL: 'http://localhost:3000',
});
Similarly to paths, you can also compose methods using the utility type
HttpSchema.Methods
:
import { HttpSchema } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
type UserMethods = HttpSchema.Methods<{
GET: {
// Method schema
};
POST: {
// Method schema
};
}>;
const interceptor = createHttpInterceptor<{
'/users': UserMethods;
}>({
worker,
baseURL: 'http://localhost:3000',
});
Declaring requests
Each method can have a request
, which defines the schema of the accepted requests. headers
, searchParams
, and
body
are supported to provide type safety when applying mocks.
import { HttpSchema, JSONValue } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
type UserCreationBody = JSONValue<{
username: string;
}>;
type UserListSearchParams = HttpSchema.SearchParams<{
username?: string;
}>;
const interceptor = createHttpInterceptor<{
'/users': {
POST: {
request: {
body: UserCreationBody;
};
// ...
};
GET: {
request: {
searchParams: UserListSearchParams;
};
// ...
};
// Other methods
};
// Other paths
}>({
worker,
baseURL: 'http://localhost:3000',
});
[!TIP]
You only need to include in the schema the properties you want to use in your mocks. Headers, search params, or body fields that are not used do not need to be declared, keeping your type definitions clean and concise.
[!IMPORTANT]
Body types cannot be declared using the keyword
interface
, because interfaces do not have implicit index signatures as types do. Part of Zimic’s JSON validation relies on index signatures. To workaround this, you can declare bodies usingtype
. As an extra step to make sure the type is a valid JSON, you can use the utility typeJSONValue
.
You can also compose requests using the utility type HttpSchema.Request
, similarly to
methods:
import { HttpSchema, JSONValue } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
type UserCreationBody = JSONValue<{
username: string;
}>;
type UserCreationRequest = HttpSchema.Request<{
body: UserCreationBody;
}>;
const interceptor = createHttpInterceptor<{
'/users': {
POST: {
request: UserCreationRequest;
};
};
}>({
worker,
baseURL: 'http://localhost:3000',
});
Declaring responses
Each method can also have a response
, which defines the schema of the returned responses. The status codes are used as
keys. headers
and body
are supported to provide type safety when applying mocks.
import { JSONValue } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
type User = JSONValue<{
username: string;
}>;
type NotFoundError = JSONValue<{
message: string;
}>;
const interceptor = createHttpInterceptor<{
'/users/:id': {
GET: {
// ...
response: {
200: { body: User };
404: { body: NotFoundError };
};
};
// Other methods
};
// Other paths
}>({
worker,
baseURL: 'http://localhost:3000',
});
[!TIP]
Similarly to Declaring requests, you only need to include in the schema the properties you want to use in your mocks. Headers, search params, or body fields that are not used do not need to be declared, keeping your type definitions clean and concise.
[!IMPORTANT]
Also similarly to Declaring requests, body types cannot be declared using the keyword
interface
, because interfaces do not have implicit index signatures as types do. Part of Zimic’s JSON validation relies on index signatures. To workaround this, you can declare bodies usingtype
. As an extra step to make sure the type is a valid JSON, you can use the utility typeJSONValue
.
You can also compose responses using the utility types HttpSchema.ResponseByStatusCode
and
HttpSchema.Response
, similarly to requests:
import { HttpSchema, JSONValue } from 'zimic';
import { createHttpInterceptor } from 'zimic/interceptor';
type User = JSONValue<{
username: string;
}>;
type NotFoundError = JSONValue<{
message: string;
}>;
type SuccessUserGetResponse = HttpSchema.Response<{
body: User;
}>;
type NotFoundUserGetResponse = HttpSchema.Response<{
body: NotFoundError;
}>;
type UserGetResponses = HttpSchema.ResponseByStatusCode<{
200: SuccessUserGetResponse;
404: NotFoundUserGetResponse;
}>;
const interceptor = createHttpInterceptor<{
'/users/:id': {
GET: {
response: UserGetResponses;
};
};
}>({
worker,
baseURL: 'http://localhost:3000',
});
interceptor.baseURL()
Returns the base URL of the interceptor.
const baseURL = interceptor.baseURL();
interceptor.<method>(path)
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
.
import { createHttpInterceptor } from 'zimic/interceptor';
const interceptor = createHttpInterceptor<{
'/users': {
GET: {
response: {
200: { body: User[] };
};
};
};
}>({
worker,
baseURL: 'http://localhost:3000',
});
// Intercept any GET requests to http://localhost:3000/users and return this response
const listHandler = interceptor.get('/users').respond({
status: 200
body: [{ username: 'diego-aquino' }],
});
Dynamic path parameters
Paths with dynamic path parameters, such as /users/:id
, are supported, but you need to specify the original path as a
type parameter to get type validation.
import { createHttpInterceptor } from 'zimic/interceptor';
const interceptor = createHttpInterceptor<{
'/users/:id': {
PUT: {
request: {
body: {
username: string;
};
};
response: {
204: {};
};
};
};
}>({
worker,
baseURL: 'http://localhost:3000',
});
interceptor.get('/users/:id'); // Matches any id
interceptor.get<'/users/:id'>(`/users/${1}`); // Only matches id 1 (notice the original path as a type parameter)
interceptor.clear()
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.
interceptor.clear();
HttpRequestHandler
HTTP request handlers allow declaring responses to return for matched intercepted requests. They also keep track of the intercepted requests and their responses, allowing checks about how many requests your application made and with which parameters.
When multiple handlers match the same method and path, the last created with
interceptor.<method>(path)
will be used.
handler.method()
Returns the method that matches this handler.
const handler = interceptor.post('/users');
const method = handler.method();
console.log(method); // 'POST'
handler.path()
Returns the path that matches this handler. The base URL of the interceptor is not included, but it is used when matching requests.
const handler = interceptor.get('/users');
const path = handler.path();
console.log(path); // '/users'
handler.with(restriction)
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 .with()
, all of them must be met, essentially creating an AND condition.
Static restrictions
const creationHandler = interceptor
.post('/users')
.with({
headers: { 'content-type': 'application/json' },
body: creationPayload,
})
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
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 example above, requests with more headers than
content-type: application/json
will still match the handler. The same applies to search params and body restrictions.
If you want to match only requests with the exact values declared, you can use exact: true
:
const creationHandler = interceptor
.post('/users')
.with({
headers: { 'content-type': 'application/json' },
body: creationPayload,
exact: true, // Only requests with these exact headers and body will match
})
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
Computed restrictions
A function is also supported to declare restrictions, in case they are dynamic.
const creationHandler = interceptor
.post('/users')
.with((request) => {
const accept = request.headers.get('accept');
return accept !== null && accept.startsWith('application');
})
.respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
The request
parameter represents the intercepted request, containing useful properties such as .body
, .headers
,
and .searchParams
, which are typed based on the interceptor schema. The function should return a boolean: true
if
the request matches the handler and should receive the mock response; false
otherwise and the request should bypass
the handler.
handler.respond(declaration)
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.
Static responses
const listHandler = interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
Computed responses
A function is also supported to declare a response, in case it is dynamic:
const listHandler = interceptor.get('/users').respond((request) => {
const username = request.searchParams.get('username');
return {
status: 200,
body: [{ username }],
};
});
The request
parameter represents the intercepted request, containing useful properties such as .body
, .headers
,
and .searchParams
, which are typed based on the interceptor schema.
handler.bypass()
Clears any response declared with .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()
.
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.
const listHandler1 = interceptor.get('/users').respond({
status: 200,
body: [],
});
const listHandler2 = interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
listHandler2.bypass();
// Now, GET requests to /users will match listHandler1 and return an empty array
listHandler2.requests(); // Still contains the intercepted requests up to the bypass
handler.clear()
Clears any response declared with .respond(declaration)
, restrictions declared with
.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.
const listHandler1 = interceptor.get('/users').respond({
status: 200,
body: [],
});
const listHandler2 = interceptor.get('/users').respond({
status: 200,
body: [{ username: 'diego-aquino' }],
});
listHandler2.clear();
// Now, GET requests to /users will match listHandler1 and return an empty array
listHandler2.requests(); // Now empty
handler.requests()
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.
const 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',
body: JSON.stringify({ username: 'new' }),
});
const updateRequests = updateHandler.requests();
expect(updateRequests).toHaveLength(1);
expect(updateRequests[0].url).toEqual('http://localhost:3000/users/1');
expect(updateRequests[0].body).toEqual({ username: 'new' });
The return by requests
are simplified objects based on the
Request
and
Response
web APIs, containing an already parsed body in
.body
, typed headers in .headers
and typed search params in .searchParams
.
If you need access to the original Request
and Response
objects, you can use the .raw
property:
const listRequests = listHandler.requests();
console.log(listRequests[0].raw); // Request{}
console.log(listRequests[0].response.raw); // Response{}
CLI
zimic --version
Displays the current version of Zimic.
zimic --help
Displays the available commands and options.
zimic browser init <publicDirectory>
Initializes the mock service worker in a public directory.
This command is necessary when using Zimic in a browser environment. It creates a mockServiceWorker.js
file in the
provided public directory, which is used to intercept requests and mock responses.
If you are using Zimic mainly in tests, we recommend adding the mockServiceWorker.js
to your .gitignore
and adding
this command to a postinstall
scripts in your package.json
. This ensures that the latest service worker script is
being used after upgrading Zimic.
Changelog
The changelog is available on our GitHub Releases page.
Development
Interested in contributing to Zimic? View our contributing guide.