Wrangler now provides createTestHarness(), an API for running integration tests against Workers built with Wrangler or the Cloudflare Vite plugin from any Node.js test runner.
The test harness starts a local Worker server with helpers for dispatching requests, resetting storage, and inspecting runtime logs.
This is useful for tests that need to:
- Route requests across multiple Workers
- Mock outbound
fetch()requests with Node.js request mocking libraries such as MSW ↗ - Run Playwright tests against a Worker
For example, this test starts two Workers and mocks an upstream API:
import { afterAll, afterEach, beforeAll, test } from "vitest";
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
import { createTestHarness } from "wrangler";
const network = setupServer();
const server = createTestHarness({
workers: [
/** Includes `"routes": ["example.com/*"]` */
{ configPath: "./workers/web/wrangler.jsonc" },
/** Includes `"routes": ["api.example.com/v1/*"]` */
{ configPath: "./workers/api/wrangler.jsonc" },
],
});
beforeAll(async () => {
network.listen({ onUnhandledRequest: "error" });
await server.listen();
});
afterEach(async () => {
network.resetHandlers();
await server.reset();
});
afterAll(async () => {
network.close();
await server.close();
});
test("routes requests to each Worker", async ({ expect }) => {
// Mock the outbound fetch used to load user profiles.
network.use(
http.get("http://identity.example.com/profile/123", ({ params }) => {
return HttpResponse.json({ id: 123, name: "Ada" });
}),
);
const apiWorkerResponse = await server.fetch(
"http://api.example.com/v1/users/123",
);
expect(await apiWorkerResponse.json()).toEqual({
id: 123,
name: "Ada",
});
const webWorkerResponse = await server.fetch("http://example.com/users/123");
expect(await webWorkerResponse.text()).toBe("Profile: Ada");
});
import { afterAll, afterEach, beforeAll, test } from "vitest";
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
import { createTestHarness } from "wrangler";
const network = setupServer();
const server = createTestHarness({
workers: [
/** Includes `"routes": ["example.com/*"]` */
{ configPath: "./workers/web/wrangler.jsonc" },
/** Includes `"routes": ["api.example.com/v1/*"]` */
{ configPath: "./workers/api/wrangler.jsonc" },
],
});
beforeAll(async () => {
network.listen({ onUnhandledRequest: "error" });
await server.listen();
});
afterEach(async () => {
network.resetHandlers();
await server.reset();
});
afterAll(async () => {
network.close();
await server.close();
});
test("routes requests to each Worker", async ({ expect }) => {
// Mock the outbound fetch used to load user profiles.
network.use(
http.get("http://identity.example.com/profile/123", ({ params }) => {
return HttpResponse.json({ id: 123, name: "Ada" });
}),
);
const apiWorkerResponse = await server.fetch(
"http://api.example.com/v1/users/123",
);
expect(await apiWorkerResponse.json()).toEqual({
id: 123,
name: "Ada",
});
const webWorkerResponse = await server.fetch("http://example.com/users/123");
expect(await webWorkerResponse.text()).toBe("Profile: Ada");
});
Cloudflare now recommends createTestHarness() for integration tests instead of unstable_startWorker() or unstable_dev(). To start a development server programmatically, use the Vite createServer() ↗ API with the Cloudflare Vite plugin.
For more information about createTestHarness(), refer to the Integration test harness guide.
Source: Cloudflare


