在不实际访问数据库的情况下测试使用 Prisma 的 NestJS 服务
Testing a NestJS Service that uses Prisma without actually accessing the database
我见过的大多数关于如何测试 Prisma 注入的 NestJS 服务的示例(例如 prisma-sample
in testing-nestjs
)都是针对“端到端”测试的。他们实际访问数据库,执行实际查询,然后在必要时回滚结果。
为了我目前的需要,我想实现较低级别的“集成”测试。
作为其中的一部分,我想从等式中删除 Prisma。我希望重点放在我的服务功能上,而不是数据库中的数据状态和 Prisma return 它的能力。
这种方法的一大优势是它避免了为特定测试精心设计“设置”查询和“拆卸”/重置操作的需要。相反,我想简单地手动指定我们对 Prisma 的期望 return.
在NestJS、Prisma、Jest组成的环境中,我该如何实现?
更新:testing-nestjs项目的作者在评论中指出该项目确实有an example of database mocking。看起来很好!其他人可能仍然有兴趣查看我链接到的 Gist,因为它包含一些其他有用的功能。
要获取对您服务的 prisma 实例的引用,请使用:
prisma = module.get<PrismaService>(PrismaService)
然后,假设你的函数调用prisma.name.findMany()
,你可以使用jest.fn().mockReturnValueOnce()
模拟(手动指定)Prisma的下一个return值:
prisma.name.findMany = jest.fn().mockReturnValueOnce([
{ id: 0, name: 'developer' },
{ id: 10, name: 'architect' },
{ id: 13, name: 'dog walker' }
]);
(当然,您可以更改上面代码中的 prisma.name.findMany
以匹配您正在调用的任何函数。)
然后,调用您正在测试的服务上的函数。例如:
expect(await service.getFirstJob("steve")).toBe('developer');
就是这样! A full code example can be found here.
Nest.js 提供了一个 API 来模拟整个服务,而 Prisma 只是其中之一。
为简单起见,假设我们直接从应用程序控制器访问 prisma。
import { Controller, Get } from '@nestjs/common';
import { DbService } from 'src/db/db.service';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(
private readonly appService: AppService,
private readonly prisma: DbService,
) {}
@Get()
async getHello(): Promise<string> {
const result = await this.prisma.user.findMany();
console.log('result', result);
return this.appService.getHello();
}
}
那么测试将是:
describe('AppController', () => {
let appController: AppController;
const mockPrisma = {
user: { findMany: () => Promise.resolve([]) },
};
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService, DbService],
})
.overrideProvider(DbService)
.useValue(mockPrisma)
.compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).resolves.toBe('Hello World!');
});
});
});
DbService 是以下文件(如此处 https://docs.nestjs.com/recipes/prisma#use-prisma-client-in-your-nestjs-services 所建议):
@Injectable()
export class DbService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
请务必使用 DbService
的实例,而不是字符串 'DbService'
。否则仍然进行数据库调用并且测试失败。
我见过的大多数关于如何测试 Prisma 注入的 NestJS 服务的示例(例如 prisma-sample
in testing-nestjs
)都是针对“端到端”测试的。他们实际访问数据库,执行实际查询,然后在必要时回滚结果。
为了我目前的需要,我想实现较低级别的“集成”测试。
作为其中的一部分,我想从等式中删除 Prisma。我希望重点放在我的服务功能上,而不是数据库中的数据状态和 Prisma return 它的能力。
这种方法的一大优势是它避免了为特定测试精心设计“设置”查询和“拆卸”/重置操作的需要。相反,我想简单地手动指定我们对 Prisma 的期望 return.
在NestJS、Prisma、Jest组成的环境中,我该如何实现?
更新:testing-nestjs项目的作者在评论中指出该项目确实有an example of database mocking。看起来很好!其他人可能仍然有兴趣查看我链接到的 Gist,因为它包含一些其他有用的功能。
要获取对您服务的 prisma 实例的引用,请使用:
prisma = module.get<PrismaService>(PrismaService)
然后,假设你的函数调用prisma.name.findMany()
,你可以使用jest.fn().mockReturnValueOnce()
模拟(手动指定)Prisma的下一个return值:
prisma.name.findMany = jest.fn().mockReturnValueOnce([
{ id: 0, name: 'developer' },
{ id: 10, name: 'architect' },
{ id: 13, name: 'dog walker' }
]);
(当然,您可以更改上面代码中的 prisma.name.findMany
以匹配您正在调用的任何函数。)
然后,调用您正在测试的服务上的函数。例如:
expect(await service.getFirstJob("steve")).toBe('developer');
就是这样! A full code example can be found here.
Nest.js 提供了一个 API 来模拟整个服务,而 Prisma 只是其中之一。
为简单起见,假设我们直接从应用程序控制器访问 prisma。
import { Controller, Get } from '@nestjs/common';
import { DbService } from 'src/db/db.service';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(
private readonly appService: AppService,
private readonly prisma: DbService,
) {}
@Get()
async getHello(): Promise<string> {
const result = await this.prisma.user.findMany();
console.log('result', result);
return this.appService.getHello();
}
}
那么测试将是:
describe('AppController', () => {
let appController: AppController;
const mockPrisma = {
user: { findMany: () => Promise.resolve([]) },
};
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService, DbService],
})
.overrideProvider(DbService)
.useValue(mockPrisma)
.compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).resolves.toBe('Hello World!');
});
});
});
DbService 是以下文件(如此处 https://docs.nestjs.com/recipes/prisma#use-prisma-client-in-your-nestjs-services 所建议):
@Injectable()
export class DbService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
请务必使用 DbService
的实例,而不是字符串 'DbService'
。否则仍然进行数据库调用并且测试失败。