测试 Prisma 应用:单元测试与集成测试
本教程共 54 篇 · 第 43 篇 · 更新于 2026-08-11 · 约 6 分钟阅读
本节目标:单测用 mock 隔离数据库,集成测试跑真库,学会快速重置与事务隔离。
测试分两层:单元测试验证函数逻辑,不碰数据库;集成测试验证真实 SQL 和关系约束,需要真库。
单元测试:mockDeep
单元测试要隔离外部依赖。mock 掉 Prisma Client,测试快且不依赖数据库,同时保留类型安全。工具用 jest-mock-extended(Vitest 用 vitest-mock-extended),核心是 mockDeep:把 PrismaClient 的每个方法都变成 mock,还能用 mockResolvedValue 指定返回值。
npm install -D jest-mock-extended
mockDeep 的好处是类型安全:mock 出来的对象和真实 PrismaClient 有一样的类型,测试里写错方法名、传错参数,编译期就报错。mock 对象上每个方法默认返回 undefined,要用 mockResolvedValue 指定返回值,或 mockRejectedValue 模拟失败。
两种组织方式:
- 单例模式:客户端集中在一个文件,测试里 jest.mock 整个模块,导出深度 mock 实例。适合已经用单例的项目,改动最小。
- 依赖注入:函数把 Prisma Client 作为参数传入,测试传 mock 实例,生产传真实例。更干净,跨测试框架可移植。
单例模式的 mock 文件长这样:
// singleton.ts
import { PrismaClient } from "../generated/prisma/client";
import { mockDeep, mockReset, DeepMockProxy } from "jest-mock-extended";
import { prisma } from "./client";
jest.mock("./client", () => ({
prisma: mockDeep<PrismaClient>(),
}));
beforeEach(() => {
mockReset(prismaMock);
});
export const prismaMock = prisma as unknown as DeepMockProxy<PrismaClient>;
再把 singleton.ts 配到 jest 的 setupFilesAfterEnv 里,每个测试文件都会自动重置 mock。
依赖注入的写法:
// context.ts
import { PrismaClient } from "../generated/prisma/client";
import { mockDeep, DeepMockProxy } from "jest-mock-extended";
export type Context = { prisma: PrismaClient };
export type MockContext = { prisma: DeepMockProxy<PrismaClient> };
export const createMockContext = (): MockContext => ({
prisma: mockDeep<PrismaClient>(),
});
// create-user.ts:prisma 通过参数注入
export async function createUser(
data: { email: string; name: string },
ctx: Context,
) {
return ctx.prisma.user.create({ data });
}
// create-user.test.ts
import { createMockContext, MockContext, Context } from "./context";
import { createUser } from "./create-user";
let mockCtx: MockContext;
let ctx: Context;
beforeEach(() => {
mockCtx = createMockContext();
ctx = mockCtx as unknown as Context;
});
test("创建用户", async () => {
mockCtx.prisma.user.create.mockResolvedValue({
id: 1,
email: "a@b.com",
name: "A",
});
await expect(createUser({ email: "a@b.com", name: "A" }, ctx)).resolves.toEqual({
id: 1,
email: "a@b.com",
name: "A",
});
});
Tip依赖注入不依赖 Jest 的模块替换机制,换 Vitest 也不用改代码。推荐默认用这个。
单测写什么?重点测函数自己的逻辑:条件分支(比如未勾选条款时拒绝注册)、参数转换、错误路径。mock 掉数据库后,测试只关心「给了这个输入,调用了哪个方法、传了什么参数」,数据库行为不归单测管。
mock 的 PrismaClient 不需要适配器,因为根本不连数据库。遇到循环依赖报错时,在 tsconfig 里开 strictNullChecks 通常能解决。
集成测试:Docker 起真库
集成测试要验证真实行为:唯一约束、外键、级联删除。用 Docker 起一个测试专用数据库:
- 写 docker-compose.yml,映射到非默认端口,避免和本地开发库冲突。
- 测试前跑
prisma migrate deploy建表。 - 跑测试,结束后销毁容器。
services:
db:
image: postgres:17-alpine
ports:
- "5433:5432"
environment:
POSTGRES_USER: prisma
POSTGRES_PASSWORD: prisma
POSTGRES_DB: tests
docker compose up -d
npx prisma migrate deploy # 对测试库应用迁移
npm test
更自动化的方案是 Testcontainers:测试代码里启动容器、等数据库就绪、跑完自动销毁,不用手动敲 docker 命令。生命周期固定四步:启动 → 迁移 → 跑测试 → 停止。
种子数据(seed)的三种来源:工厂函数按测试定制数据、静态 JSON fixture、共享种子脚本做基线。简单场景用工厂函数最灵活。
集成测试的用例要覆盖数据库强约束:唯一字段重复插入抛 P2002、外键不存在的关联写不进去、级联删除真的删干净。这些行为 mock 测不出来,只有真库能验证。
测试间数据隔离:TRUNCATE 与事务回滚
集成测试最烦的是数据残留。两种主流隔离:
- TRUNCATE 重置。每个测试前清空表,Postgres 上最快:
beforeEach(async () => {
await prisma.$executeRaw`TRUNCATE "User", "Post" RESTART IDENTITY CASCADE`;
});
- 事务回滚。每个测试开一个事务,测试结束抛错回滚,数据自动还原:
test("创建用户", async () => {
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: { email: "a@b.com" } });
expect(user.id).toBeDefined();
throw new Error("rollback"); // 回滚,不留数据
});
});
Note事务回滚有个前提:被测代码必须复用同一个事务客户端(tx),不能自己另起事务。做不到就用 TRUNCATE。
TRUNCATE 要按外键顺序清空,或直接 CASCADE 一把梭;RESTART IDENTITY 顺手重置自增主键,保证每次测试的主键从 1 开始,断言好写。deleteMany 也能清数据,各数据库通用,但比 TRUNCATE 慢,适合表少、行少的情况。
迁移测试
迁移文件本身也要测。做法:在 CI 里对测试库(或影子数据库 shadow database)跑 migrate deploy,再用 migrate diff 对比迁移历史与 Schema,确认无漂移:
npx prisma migrate deploy
npx prisma migrate diff \
--from-migrations prisma/migrations \
--to-schema prisma/schema.prisma \
--exit-code
diff 有输出说明迁移历史与 Schema 不一致,CI 就该失败。
再往上还有端到端(E2E)测试:Supertest 打 HTTP 接口、Playwright 跑浏览器,验证完整链路。E2E 也复用同一个测试库,跑完一起清理。覆盖率用 vitest —coverage 或 c8 统计,重点关注数据访问层,别为了数字好看硬凑。
整套测试金字塔自下而上:单测最多最快,集成测试次之,E2E 最少最慢。数据库相关逻辑优先写集成测试,因为 mock 会掩盖真实 SQL 的问题。
Tip测试脚本串起来:docker compose up -d → migrate deploy → 跑测试 → docker compose down。任何一步失败都中断。
参考来源
- Prisma 官方文档:Unit testing / Integration testing
- Mapagam:Testing Prisma Applications
- Tech Insider:Prisma ORM Tutorial: Build a Type-Safe API in 13 Steps