测试工具
用于集成测试和端到端测试的测试实用工具
Test Utils 插件提供了针对 Better Auth 编写集成测试和端到端测试的辅助工具。它包括工厂函数、数据库辅助函数、认证辅助函数以及 OTP 捕获功能。
此插件仅适用于测试环境。请勿在生产环境中使用。
安装
在您的 auth 配置中添加该插件
import { betterAuth } from "better-auth"
import { testUtils } from "better-auth/plugins"
export const auth = betterAuth({
// ... 其他配置选项
plugins: [
testUtils()
]
})通过上下文访问测试辅助工具
const ctx = await auth.$context
const test = ctx.test使用方法
工厂函数
工厂函数创建对象但不写入数据库。用它们生成具有合理默认值的测试数据。
createUser
创建一个带有默认值(可被覆盖)的用户对象。
// 使用默认值创建用户
const user = test.createUser()
// { id: "...", email: "user-xxx@example.com", name: "Test User", emailVerified: true, ... }
// 使用自定义值创建用户
const user = test.createUser({
email: "alice@example.com",
name: "Alice",
emailVerified: false
})createOrganization
创建一个组织对象。仅在安装了组织插件时可用。
const org = test.createOrganization({
name: "Acme Corp",
slug: "acme-corp"
})数据库辅助函数
数据库辅助函数可将测试数据保存到数据库或从数据库中删除数据。
saveUser
将用户保存到数据库。
const user = test.createUser({ email: "test@example.com" })
const savedUser = await test.saveUser(user)deleteUser
从数据库中删除用户。
await test.deleteUser(user.id)saveOrganization
将组织保存到数据库。仅在安装了组织插件时可用。
const org = test.createOrganization({ name: "Test Org" })
const savedOrg = await test.saveOrganization(org)deleteOrganization
从数据库中删除组织。仅在安装了组织插件时可用。
await test.deleteOrganization(org.id)addMember
将用户添加为组织成员。仅在安装了组织插件时可用。
const member = await test.addMember({
userId: user.id,
organizationId: org.id,
role: "admin"
})认证辅助函数
认证辅助函数用于创建认证会话,以测试受保护路由。
login
为用户创建会话并返回会话详情、请求头、cookies 和令牌。
const { session, user, headers, cookies, token } = await test.login({
userId: user.id
})
// session - 包含 userId、token 等信息的会话对象
// user - 用户对象
// headers - 带有会话 cookie 的 Headers 对象(用于 fetch/Request)
// cookies - Cookie 数组(用于 Playwright/Puppeteer)
// token - 会话令牌字符串getAuthHeaders
返回带有会话 cookie 的 Headers 对象,适合进行认证请求。
const headers = await test.getAuthHeaders({ userId: user.id })
// 与 auth API 一起使用
const session = await auth.api.getSession({ headers })
// 与 fetch 一起使用
const response = await fetch("/api/protected", { headers })getCookies
返回符合浏览器测试工具(如 Playwright 和 Puppeteer)要求的 cookie 对象数组。
const cookies = await test.getCookies({
userId: user.id,
domain: "localhost" // 可选,默认使用 baseURL 域名
})
// Playwright 示例
await context.addCookies(cookies)
// Puppeteer 示例
for (const cookie of cookies) {
await page.setCookie(cookie)
}每个 cookie 对象包含:
name- Cookie 名称(例如 "better-auth.session_token")value- Cookie 值domain- Cookie 域名path- Cookie 路径(默认为 "/")httpOnly- 是否为 HttpOnly cookiesecure- 是否需要 HTTPSsameSite- SameSite 属性("Lax"、"Strict" 或 "None")
OTP 捕获
当设置 captureOTP: true 时,插件会被动捕获创建的 OTP。这让你可以在测试中获取 OTP,而无需模拟邮件或短信发送。
OTP 捕获为被动过程——不会阻止 OTP 通过你配置的 sendVerificationOTP 函数发送。它仅存储一份用于测试获取的副本。
import { betterAuth } from "better-auth"
import { testUtils, emailOTP } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
testUtils({ captureOTP: true }),
emailOTP({
async sendVerificationOTP({ email, otp }) {
// 你的邮件发送逻辑
}
})
]
})getOTP
通过标识符(邮箱或手机号)获取捕获的 OTP。
// 发送 OTP
await auth.api.sendVerificationOTP({
body: { email: "user@example.com", type: "sign-in" }
})
// 获取捕获的 OTP
const otp = test.getOTP("user@example.com")
// "123456"选项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
captureOTP | boolean | false | 是否启用 OTP 捕获,用于测试验证流程 |
示例
集成测试(Vitest)
import { describe, it, expect, beforeAll } from "vitest"
import { auth } from "./auth"
import type { TestHelpers } from "better-auth/plugins"
describe("protected route", () => {
let test: TestHelpers
beforeAll(async () => {
const ctx = await auth.$context
test = ctx.test
})
it("应该为认证请求返回用户数据", async () => {
// 准备数据
const user = test.createUser({ email: "test@example.com" })
await test.saveUser(user)
// 获取认证请求头
const headers = await test.getAuthHeaders({ userId: user.id })
// 测试认证请求
const session = await auth.api.getSession({ headers })
expect(session?.user.id).toBe(user.id)
// 清理
await test.deleteUser(user.id)
})
})端到端测试(Playwright)
import { test, expect } from "@playwright/test"
import { auth } from "./auth"
test("仪表盘显示用户名", async ({ context, page }) => {
const ctx = await auth.$context
const testUtils = ctx.test
// 创建并保存用户
const user = testUtils.createUser({
email: "e2e@example.com",
name: "E2E User"
})
await testUtils.saveUser(user)
// 获取 cookies 并注入浏览器
const cookies = await testUtils.getCookies({
userId: user.id,
domain: "localhost"
})
await context.addCookies(cookies)
// 导航至受保护页面
await page.goto("/dashboard")
// 断言用户名可见
await expect(page.getByText("E2E User")).toBeVisible()
// 清理
await testUtils.deleteUser(user.id)
})OTP 验证测试
import { describe, it, expect, beforeAll, beforeEach } from "vitest"
import { auth } from "./auth"
import type { TestHelpers } from "better-auth/plugins"
describe("OTP 验证", () => {
let test: TestHelpers
beforeAll(async () => {
const ctx = await auth.$context
test = ctx.test
})
beforeEach(() => {
test.clearOTPs()
})
it("应该使用捕获的 OTP 验证邮箱", async () => {
const email = "otp-test@example.com"
const user = test.createUser({ email, emailVerified: false })
await test.saveUser(user)
// 请求 OTP
await auth.api.sendVerificationOTP({
body: { email, type: "email-verification" }
})
// 获取捕获的 OTP
const otp = test.getOTP(email)
expect(otp).toBeDefined()
// 验证邮箱
await auth.api.verifyEmail({
body: { email, otp }
})
// 清理
await test.deleteUser(user.id)
})
})