Jest DOM-style assertions for native Fetch API requests and responses.
expect-fetch adds expressive HTTP matchers to Vitest without mocking
globalThis.fetch. Use it to test Next.js route handlers, Remix loaders,
SvelteKit and Astro endpoints, Hono applications, Cloudflare Workers, or any
code that produces native Request or Response objects.
import 'expect-fetch/vitest';
const response = await app.request('/users/123');
expect(response).toHaveStatus(200);
expect(response).toHaveHeader('content-type', /json/);
await expect(response).toHaveJson({
id: '123',
name: 'Ada',
});Request assertions use the same API:
expect(request).toHaveMethod('POST');
expect(request).toHaveUrl('/users?page=2');
expect(request).toHaveQuery({ page: '2' });
expect(request).toHaveHeader('authorization', /^Bearer /);
await expect(request).toHaveJson({ name: 'Ada' });npm install --save-dev expect-fetchAdd the integration to a Vitest setup file:
// test/setup.ts
import 'expect-fetch/vitest';// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
setupFiles: ['./test/setup.ts'],
},
});You can also import the integration directly in an individual test file.
expect(response).toHaveStatus(201);Header names are case-insensitive. Values can be exact strings or regular expressions. Requests and responses are supported.
expect(response).toHaveHeader('content-type');
expect(response).toHaveHeader('content-type', /application\/json/);
expect(response).toHaveHeader('x-request-id', 'req_123');The request or response is cloned before its body is read, so the matcher does not consume the original body. Vitest asymmetric matchers are supported.
await expect(response).toHaveJson({
id: expect.any(String),
role: 'admin',
});await expect(response).toHaveText('Not found');
await expect(response).toHaveText(/not found/i);Repeated fields are represented as arrays. Requests and responses are supported.
await expect(request).toHaveFormData({
name: 'Ada',
roles: ['admin', 'author'],
avatar: expect.any(File),
});Method comparison is case-insensitive.
expect(request).toHaveMethod('POST');Absolute URLs, URL objects, regular expressions, and relative URLs containing
the path, query, and fragment are supported.
expect(request).toHaveUrl('/users?page=2');
expect(request).toHaveUrl(/^https:\/\/example\.com\/users/);Query values are decoded. Repeated parameters are represented as arrays.
expect(request).toHaveQuery({
q: 'Ada Lovelace',
tag: ['math', 'code'],
});Without an explicit status, any status from 300 through 399 is accepted.
expect(response).toRedirectTo('/login');
expect(response).toRedirectTo('/login', 307);expect(response).toSetCookie('session', {
value: /^eyJ/,
path: '/',
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 3600,
});Supported expectations are value, domain, path, expires, maxAge,
secure, httpOnly, and sameSite. Omitted fields are not checked.
domain and sameSite comparisons are case-insensitive, and an obsolete
leading dot in a domain expectation is ignored. Date expiration expectations
are compared at the one-second precision provided by HTTP dates.
Set-Cookie is intentionally supported for server-side response tests. Browsers
do not expose this response header to client-side JavaScript.
Fetch mocking tools fake the server that your code calls. expect-fetch
verifies the Response produced by the server or handler you are building.
They solve different problems and can be used together.
- Node.js 20 or newer
- Vitest 2, 3, or 4
- Any framework using standards-compatible Fetch API objects
The matchers use structural checks rather than instanceof Response, allowing
responses created in another JavaScript realm or by compatible runtimes.
Failure diagnostics redact Authorization, Proxy-Authorization, Cookie,
Set-Cookie, and credential-like headers (including API key, token, secret, and
password) so credentials do not leak into test or CI logs.
MIT
See CONTRIBUTING.md for development and release guidance. Please use the private process in SECURITY.md to report vulnerabilities.