使用Sinon Stub Prisma Client方法未生效的问题排查
问题描述
我正尝试为一个通过Prisma Client保存任务的类编写单元测试,相关代码如下:
export class TaskHandler { ... public async save(ctx: PrismaClient, input: TaskInput): Promise<Task> { if (input.label == null || input.number == null) { throw new Error('Input not complete'); } return ctx.task.create({ data: { label: input.label, number: input.number, }, }); } }
我使用Sinon对相关方法进行Stub,单元测试代码如下:
import { stub } from 'sinon'; ... describe('Tasks', () => { const prismaClient = new PrismaClient(); describe('Task handler', () => { it('should save a item', async () => { const instance = new TaskHandler(); const task: TaskInput = { label: 'test', number: 12, }; const resultTask: Task = { id: '1', label: 'test', number: 12, }; const stubInstance = stub(prismaClient.task, 'create').resolves( resultTask, ); const result = await instance.save(prismaClient, task); console.log(JSON.stringify(result)); stubInstance.reset(); }); }); });
但调用TaskHandler的save方法时,Prisma仍在验证数据库Schema,推测原create方法仍被执行而非返回Stub结果,请问我哪里操作有误?
问题原因与解决办法
核心问题
Prisma Client的task属性是动态生成的代理对象,直接用Sinon stub prismaClient.task.create不会生效——因为代理会拦截属性访问,每次访问prismaClient.task都会返回一个新的实例,你stub的只是某一次获取到的实例上的create方法,后续调用时拿到的是新对象,原方法依然会执行。
解决步骤
方案一:使用模拟对象替代真实PrismaClient实例
单元测试应完全隔离数据库依赖,直接创建一个带有stub方法的模拟对象传入save:import { stub } from 'sinon'; ... describe('Tasks', () => { describe('Task handler', () => { it('should save an item', async () => { const instance = new TaskHandler(); const task: TaskInput = { label: 'test', number: 12, }; const resultTask: Task = { id: '1', label: 'test', number: 12, }; // 创建模拟的PrismaClient对象 const mockPrisma = { task: { create: stub().resolves(resultTask) } } as unknown as PrismaClient; const result = await instance.save(mockPrisma, task); console.log(JSON.stringify(result)); // 验证create方法是否按预期调用 mockPrisma.task.create.calledWith({ data: { label: 'test', number: 12 } }).should.be.true; }); }); });方案二:Stub PrismaClient原型上的方法
如果必须使用真实实例结构,可以直接Stub原型上的方法,确保所有实例都能应用stub:import { stub } from 'sinon'; ... describe('Tasks', () => { let stubCreate: sinon.SinonStub; before(() => { // 在所有测试前Stub原型上的create方法 stubCreate = stub(PrismaClient.prototype.task, 'create'); }); after(() => { // 测试完成后恢复原型 stubCreate.restore(); }); describe('Task handler', () => { it('should save an item', async () => { const instance = new TaskHandler(); const prismaClient = new PrismaClient(); const task: TaskInput = { label: 'test', number: 12, }; const resultTask: Task = { id: '1', label: 'test', number: 12, }; stubCreate.resolves(resultTask); const result = await instance.save(prismaClient, task); console.log(JSON.stringify(result)); }); }); });
内容的提问来源于stack exchange,提问作者glm9637
相关产品推荐
相关产品推荐

