集成测试可能需要在运行前向本地存储写入数据。它也可能依赖你不想在测试期间调用的外部服务。使用测试 harness 准备这些状态并替换这些依赖。
worker.getEnv() 返回为 Worker 配置的变量、密钥和绑定(binding)。你可以为 server.getWorker() 指定类型,以便这些值带有类型。然后使用返回的存储绑定直接从测试中填充数据:
const apiWorker = server.getWorker("api-worker");
const env = await apiWorker.getEnv();
await env.USERS.put("123", JSON.stringify({ name: "Ada" }));const apiWorker = server.getWorker<ApiEnv>("api-worker");
const env = await apiWorker.getEnv();
await env.USERS.put("123", JSON.stringify({ name: "Ada" }));使用 worker.applyD1Migrations(bindingName) 从 Wrangler 配置读取 D1 绑定的迁移设置。它使用配置的 migrations_dir 和 migrations_pattern。没有这些选项时,它会读取相对于配置文件的 migrations 目录中的 .sql 文件。
在存储重置后调用它,以应用尚未运行的迁移。然后使用 worker.getEnv() 访问数据库并填充所需行。
const apiWorker = server.getWorker("api-worker");
beforeEach(async () => {
await apiWorker.applyD1Migrations("DATABASE");
const env = await apiWorker.getEnv();
await env.DATABASE.prepare(
"INSERT INTO daily_reports (date, user_ids) VALUES (?, ?)",
)
.bind("2026-05-29", JSON.stringify(["123", "456"]))
.run();
});const apiWorker = server.getWorker<ApiEnv>("api-worker");
beforeEach(async () => {
await apiWorker.applyD1Migrations("DATABASE");
const env = await apiWorker.getEnv();
await env.DATABASE.prepare(
"INSERT INTO daily_reports (date, user_ids) VALUES (?, ?)",
)
.bind("2026-05-29", JSON.stringify(["123", "456"]))
.run();
});worker.getDurableObjectStorage() 让你访问基于 SQLite 的 Durable Object 实例的存储。传入其绑定名称或导出的类名。然后按名称或 ID 选择实例。
返回的 handle 会在 Durable Object 内执行 SQL。用它在测试前填充实例,或在 Worker 运行后检查其状态。
const worker = server.getWorker("api-worker");
const storage = await worker.getDurableObjectStorage("COUNTER", {
name: "user-123",
});
await storage.exec(
"INSERT INTO counters (id, value) VALUES (?, ?)",
"user-123",
0,
);
await worker.fetch("/counter/user-123");
const rows = await storage.exec(
"SELECT value FROM counters WHERE id = ?",
"user-123",
);
expect(rows).toEqual([{ value: 1 }]);const worker = server.getWorker<ApiEnv>("api-worker");
const storage = await worker.getDurableObjectStorage("COUNTER", {
name: "user-123",
});
await storage.exec(
"INSERT INTO counters (id, value) VALUES (?, ?)",
"user-123",
0,
);
await worker.fetch("/counter/user-123");
const rows = await storage.exec<{ value: number }>(
"SELECT value FROM counters WHERE id = ?",
"user-123",
);
expect(rows).toEqual([{ value: 1 }]);测试 harness 会将 Worker 中的出站 fetch() 请求代理到 Node 环境中的 globalThis.fetch() 函数。这允许你拦截这些请求,并在测试中返回可预测的响应。
以下示例使用 vi.spyOn() mock 单个请求。但你也可以根据偏好使用 Mock Service Worker (MSW) 拦截这些请求。
import { afterEach, test, vi } from "vitest";
afterEach(() => {
vi.restoreAllMocks();
});
test("loads a user profile", async ({ expect }) => {
vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => {
const request = new Request(input, init);
if (request.url === "http://identity.example.com/profile/123") {
return Response.json({ id: "123", name: "Ada" });
}
throw new Error(`Unexpected request: ${request.method} ${request.url}`);
});
const worker = server.getWorker();
const response = await worker.fetch("/users/123");
expect(await response.json()).toEqual({ id: "123", name: "Ada" });
});import { afterEach, test, vi } from "vitest";
afterEach(() => {
vi.restoreAllMocks();
});
test("loads a user profile", async ({ expect }) => {
vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => {
const request = new Request(input, init);
if (request.url === "http://identity.example.com/profile/123") {
return Response.json({ id: "123", name: "Ada" });
}
throw new Error(`Unexpected request: ${request.method} ${request.url}`);
});
const worker = server.getWorker();
const response = await worker.fetch("/users/123");
expect(await response.json()).toEqual({ id: "123", name: "Ada" });
});当你想控制绑定行为时,请使用 bindingOverrides。它会将绑定路由到在 harness 内运行的测试 Worker。例如,测试 Worker 可以替换 Browser Rendering 绑定,并在不启动浏览器的情况下返回已知截图。
测试 Worker 也可以暴露用于配置其行为的 JSRPC 方法。使用 worker.getExport() 访问测试的默认导出。
const server = createTestHarness({
workers: [
{
configPath: "./workers/web/wrangler.jsonc",
bindingOverrides: { BROWSER: "mock-browser" },
},
// The test Worker (mock-browser) that replaces the Browser Rendering binding
{ configPath: "./workers/mock-browser/wrangler.jsonc" },
],
});
// Access the test Worker to configure its behavior or assert its state.
const mockBrowser = await server.getWorker("mock-browser").getExport();
// Configure the screenshot to be returned through the overridden binding.
await mockBrowser.setScreenshot([137, 80, 78, 71]);
const response = await server.fetch("/reports/2026-05-29.png");
expect(await response.bytes()).toEqual(Uint8Array.from([137, 80, 78, 71]));const server = createTestHarness({
workers: [
{
configPath: "./workers/web/wrangler.jsonc",
bindingOverrides: { BROWSER: "mock-browser" },
},
// The test Worker (mock-browser) that replaces the Browser Rendering binding
{ configPath: "./workers/mock-browser/wrangler.jsonc" },
],
});
// Access the test Worker to configure its behavior or assert its state.
const mockBrowser = await server
.getWorker<unknown, typeof import("../workers/mock-browser")>("mock-browser")
.getExport();
// Configure the screenshot to be returned through the overridden binding.
await mockBrowser.setScreenshot([137, 80, 78, 71]);
const response = await server.fetch("/reports/2026-05-29.png");
expect(await response.bytes()).toEqual(Uint8Array.from([137, 80, 78, 71]));