关于 encore-testing
encore-testing 面向使用 Vitest 测试 Encore.ts 应用的开发者,说明如何借助 Encore 测试基础设施,并覆盖端点、数据库、服务调用、身份验证、Pub/Sub、定时任务和外部依赖。
主要功能
- 01Vitest 配置与测试运行
- 02Encore 管理的测试基础设施
- 03端点和数据库测试
- 04服务调用与身份验证测试
- 05Pub/Sub 和定时函数测试
- 06模拟外部依赖
适用场景
- 01通过直接调用函数测试端点
- 02在隔离测试中验证数据库操作
- 03检查需身份验证的端点行为
- 04测试 Pub/Sub 事件和定时任务函数
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ encoredev/ skills --skill encore-testing安装后在 Claude Code 中输入/encore-testing确认已加载。
详细介绍
测试 Encore.ts 应用
说明
Encore.ts 使用标准的 TypeScript 测试工具。推荐使用 Vitest。
设置 Vitest
npm install -D vitest
添加到 package.json:
{
"scripts": {
"test": "vitest"
}
}
测试 API 端点
// api.test.ts
import { describe, it, expect } from "vitest";
import { hello } from "./api";
describe("hello endpoint", () => {
it("returns a greeting", async () => {
const response = await hello();
expect(response.message).toBe("Hello, World!");
});
});
运行测试
# Run with Encore (recommended - sets up infrastructure)
encore test
# Or run directly with npm
npm test
建议使用 encore test,因为它可以:
- 自动设置测试数据库
- 为每项测试提供隔离的基础设施
- 处理服务依赖项
使用请求参数进行测试
// api.test.ts
import { describe, it, expect } from "vitest";
import { getUser } from "./api";
describe("getUser endpoint", () => {
it("returns the user by ID", async () => {
const user = await getUser({ id: "123" });
expect(user.id).toBe("123");
expect(user.name).toBeDefined();
});
});
测试数据库操作
Encore 会提供隔离的测试数据库:
// user.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { createUser, getUser, db } from "./user";
describe("user operations", () => {
beforeEach(async () => {
// Clean up before each test
await db.exec`DELETE FROM users`;
});
it("creates and retrieves a user", async () => {
const created = await createUser({ email: "[email protected]", name: "Test" });
const retrieved = await getUser({ id: created.id });
expect(retrieved.email).toBe("[email protected]");
});
});
测试服务间调用
// order.test.ts
import { describe, it, expect } from "vitest";
import { createOrder } from "./order";
describe("order service", () => {
it("creates an order and notifies user service", async () => {
// Service calls work normally in tests
const order = await createOrder({
userId: "user-123",
items: [{ productId: "prod-1", quantity: 2 }],
});
expect(order.id).toBeDefined();
expect(order.status).toBe("pending");
});
});
测试错误情况
import { describe, it, expect } from "vitest";
import { getUser } from "./api";
import { APIError } from "encore.dev/api";
describe("error handling", () => {
it("throws NotFound for missing user", async () => {
await expect(getUser({ id: "nonexistent" }))
.rejects
.toThrow("user not found");
});
it("throws with correct error code", async () => {
try {
await getUser({ id: "nonexistent" });
} catch (error) {
expect(error).toBeInstanceOf(APIError);
expect((error as APIError).code).toBe("not_found");
}
});
});
测试经过身份验证的端点
模拟 getAuthData,以指定用户身份直接调用 auth: true 端点:
import { describe, it, expect, vi } from "vitest";
import * as auth from "~encore/auth";
import { getProfile } from "./api";
it("returns the caller's profile", async () => {
vi.spyOn(auth, "getAuthData").mockImplementation(() => ({ userID: "123" }));
const profile = await getProfile();
expect(profile.id).toBe("123");
});
此模拟仅影响测试进程中直接读取 getAuthData() 的代码。通过 ~encore/clients 发起的调用会经过 Encore 运行时,而运行时无法看到此模拟,因此以这种方式调用 auth: true 端点会报错:“endpoint requires auth but none provided”。请改为显式传入身份验证数据:
import { user } from "~encore/clients";
await user.follow({ username: "jake" }, { authData: { userID: "123" } });
测试 Pub/Sub
// notifications.test.ts
import { describe, it, expect, vi } from "vitest";
import { orderCreated } from "./events";
describe("pub/sub", () => {
it("publishes order created event", async () => {
const messageId = await orderCreated.publish({
orderId: "order-123",
userId: "user-456",
total: 9999,
});
expect(messageId).toBeDefined();
});
});
测试 Cron 作业
测试底层函数,而不是 cron 计划:
// cleanup.test.ts
import { describe, it, expect } from "vitest";
import { cleanupExpiredSessions } from "./cleanup";
describe("cleanup job", () => {
it("removes expired sessions", async () => {
// Create some expired sessions first
await createExpiredSession();
// Call the endpoint directly
await cleanupExpiredSessions();
// Verify cleanup happened
const remaining = await countSessions();
expect(remaining).toBe(0);
});
});
模拟外部服务
import { describe, it, expect, vi, beforeEach } from "vitest";
import { sendWelcomeEmail } from "./email";
// Mock external API
vi.mock("./external-email-client", () => ({
send: vi.fn().mockResolvedValue({ success: true }),
}));
describe("email service", () => {
it("sends welcome email", async () => {
const result = await sendWelcomeEmail({ userId: "123" });
expect(result.sent).toBe(true);
});
});
测试配置
创建 vite.config.ts(使用 ~encore 导入时必需):
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
resolve: {
alias: {
"~encore": path.resolve(__dirname, "./encore.gen"),
},
},
test: {
globals: true,
environment: "node",
include: ["**/*.test.ts"],
coverage: {
reporter: ["text", "json", "html"],
},
},
});
VS Code 集成
安装 Vitest 扩展,并添加到 .vscode/settings.json:
{
"vitest.commandLine": "encore test"
}
**注意:**对于 VS Code 测试资源管理器,请禁用文件级并行,以避免端口冲突:
// vite.config.ts
export default defineConfig({
// ...
test: {
fileParallelism: false, // Disable for VS Code
// ...
},
});
在 CI 中重新启用:encore test --fileParallelism=true
指南
- 使用
encore test运行测试并设置基础设施 - 每个测试文件都会获得独立的数据库事务(完成后回滚)
- 直接将 API 端点作为函数调用来测试
- 服务间调用在测试中可以正常工作
- 模拟外部依赖项(第三方 API、电子邮件服务等)
- 不要模拟 Encore 基础设施(数据库、Pub/Sub),请使用实际服务
评论
支持 Markdown