跳转到内容
搜索文档

已知问题

最后更新 查看 MarkdownAgent 设置

Workers Vitest pool 目前处于公开 Beta 阶段。以下是 Cloudflare 已知并正在修复的问题:

覆盖率

不支持通过 V8 的原生代码覆盖率。必须使用 Istanbul 的插桩代码覆盖率。设置说明请参阅 Vitest 覆盖率文档

假定时器

Vitest 的假定时器不适用于 KV、R2 和 cache 模拟器。例如,无法通过推进假时间来使 KV 键过期。

exports 和 Durable Objects 中使用动态 import() 语句

在使用 exports.default.fetch() 编写集成测试时,在 export default { ... } 处理器内部,或在 Durable Object 事件处理器内部,动态 import() 语句无法正常工作。必须直接导入并调用处理器,或在全局作用域中使用静态 import 语句。

WebSockets

在按文件存储隔离的情况下,Durable Objects 不支持 WebSockets。解决方法是以共享存储方式运行测试,使用 --max-workers=1 --no-isolate

存储隔离

存储隔离按测试文件进行。测试运行器会在每个测试文件结束时撤销对存储的所有写入,详见隔离与并发文档。Cloudflare 建议采取以下措施以避免常见问题:

等待所有存储操作

始终 await 所有读取或写入存储服务的 Promise

// Example: Seed data
beforeAll(async () => {
	await env.KV.put("message", "test message");
	await env.R2.put("file", "hello-world");
});

显式标记资源释放

调用 Service Worker 或 Durable Object 的 RPC 方法且返回非原始值(例如对象或扩展 RpcTarget 的类)时,使用 using 关键字显式标记资源何时可以释放。请参阅此示例测试以及显式资源管理了解更多详情。

using result = await stub.getCounter();

消费响应体

通过 fetchR2.get() 发起请求时,即使不断言其内容,也要消费完整的响应体。例如:

test("check if file exists", async () => {
	await env.R2.put("file", "hello-world");
	const response = await env.R2.get("file");

	expect(response).not.toBe(null);
	// Consume the response body even if you are not asserting it
	await response.text();
});

ctx.exports 上缺少属性

ctx.exports 属性提供对 main Worker 导出的访问。Workers Vitest 集成尝试通过 esbuild 静态分析 Worker 源代码来自动推断这些导出。然而,复杂的构建设置(例如使用 esbuild 无法跟踪的虚拟模块或通配符重新导出)可能导致 ctx.exports 对象上缺少属性。

例如,考虑一个 Worker 使用通配符导出从虚拟模块重新导出入口点:

// index.ts
export * from "@virtual-module";

在这种情况下,来自 @virtual-module 的任何导出(例如 MyEntrypoint)都无法自动推断,将不会出现在 ctx.exports 中。

解决方法是在 Vitest 配置中添加 additionalExports 选项:

import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineConfig({
	plugins: [
		cloudflareTest({
			wrangler: { configPath: "./wrangler.jsonc" },
			additionalExports: {
				MyEntrypoint: "WorkerEntrypoint",
			},
		}),
	],
});

additionalExports 选项是一个映射,键为导出名称,值为导出类型("WorkerEntrypoint""DurableObject""WorkflowEntrypoint")。

模块解析

如果遇到模块解析问题,例如:Error: Cannot use require() to import an ES ModuleError: No such module,可以使用 deps.optimizer 选项打包这些依赖:

import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineConfig({
	plugins: [
		cloudflareTest({
			// ...
		}),
	],
	test: {
		deps: {
			optimizer: {
				ssr: {
					enabled: true,
					include: ["your-package-name"],
				},
			},
		},
	},
});

示例请参阅示例页面。

从 global setup 文件导入模块

虽然 Vitest 已配置为在 workerd 运行时解析包,但 global setup 文件在 Node.js 环境中运行。导入 Postgres.js 等包时可能出现问题,该包为 workerd 导出了非 Node 版本。 解决方法是可以创建一个包装器,使用 Vite 的 SSR 模块加载器在正确条件下导入 global setup 文件。然后调整 Vitest 配置以指向该包装器。例如:

// File: global-setup-wrapper.ts
import { createServer } from "vite";

// Import the actual global setup file with the correct setup
const mod = await viteImport("./global-setup.ts");

export default mod.default;

// Helper to import the file with default node setup
async function viteImport(file: string) {
	const server = await createServer({
		root: import.meta.dirname,
		configFile: false,
		server: { middlewareMode: true, hmr: false, watch: null, ws: false },
		optimizeDeps: { noDiscovery: true },
		clearScreen: false,
	});
	const mod = await server.ssrLoadModule(file);
	await server.close();
	return mod;
}
// File: vitest.config.ts
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineConfig({
	plugins: [
		cloudflareTest({
			// ...
		}),
	],
	test: {
		// Replace the globalSetup with the wrapper file
		globalSetup: ["./global-setup-wrapper.ts"],
	},
});

这篇文档对您有帮助吗?