在不实际访问数据库的情况下测试使用 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'。否则仍然进行数据库调用并且测试失败。