测试使用 Prisma 的 NestJS 服务,而无需实际访问数据库

rin*_*ogo 14 jestjs nestjs prisma

我见过的大多数关于如何测试 Prisma 注入的 NestJS 服务的示例(例如在 参考资料prisma-sampletesting-nestjs)都是“端到端”测试。他们实际上访问数据库,执行实际查询,然后在必要时回滚结果。

对于我当前的需求,我想实现较低级别的“集成”测试。

作为其中的一部分,我想从等式中删除 Prisma。我希望重点关注服务的功能,而不是数据库内数据的状态以及 Prisma 返回数据的能力。

这种方法的一大优点是它消除了为特定测试精心设计“设置”查询和“拆卸”/重置操作的需要。相反,我想简单地手动指定我们期望 Prisma 返回的内容。

在由 NestJS、Prisma 和 Jest 组成的环境中,我应该如何实现这一点?


更新:testing-nestjs 项目的作者在评论中指出该项目确实有数据库模拟的示例。看起来很好!其他人可能仍然有兴趣查看我链接到的要点,因为它包含一些其他有用的功能。

rin*_*ogo 16

要获取对服务的 prisma 实例的引用,请使用:

prisma = module.get<PrismaService>(PrismaService)
Run Code Online (Sandbox Code Playgroud)

然后,假设您的函数调用prisma.name.findMany(),您可以使用jest.fn().mockReturnValueOnce()来模拟(手动指定)Prisma 的下一个返回值:

prisma.name.findMany = jest.fn().mockReturnValueOnce([
    { id: 0, name: 'developer' },
    { id: 10, name: 'architect' },
    { id: 13, name: 'dog walker' }
]);
Run Code Online (Sandbox Code Playgroud)

(当然,您可以更改prisma.name.findMany上面的代码以匹配您调用的任何函数。)

然后,调用您正在测试的服务上的函数。例如:

expect(await service.getFirstJob("steve")).toBe('developer');
Run Code Online (Sandbox Code Playgroud)

就是这样!完整的代码示例可以在这里找到


Dav*_*ira 11

您还可以使用包来模拟整个 Prisma 客户端,这是 Prisma 官方文档jest-mock-extended中使用的方法。

假设您已经按照 NestJS文档PrismaService中的建议创建了一个,测试将是:

import { Test, TestingModule } from '@nestjs/testing'
import { PrismaClient } from '@prisma/client'
import { mockDeep, DeepMockProxy } from 'jest-mock-extended'
    
describe('UserService', () => {
  let service: UserService;
  let prisma: DeepMockProxy<PrismaClient>;
    
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UserService, PrismaService],
    })
      .overrideProvider(PrismaService)
      .useValue(mockDeep<PrismaClient>())
      .compile();
    
    service = module.get(UserService);
    prisma = module.get(PrismaService);
  });
    

  it('returns users', () => {
    const testUsers = [];

    prisma.user.findMany.mockResolvedValueOnce(testUsers);

    expect(service.findAll()).resolves.toBe(testUsers);
  });
});
Run Code Online (Sandbox Code Playgroud)

这种方法的好处之一是您可以使用Jest Mock 函数 API轻松地在 Prisma 客户端上进行模拟。


Ily*_*ski 6

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

那么测试将是:

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!');
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

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();
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

请务必使用 的实例DbService,而不是 string 'DbService'。否则仍然会进行数据库调用并且测试失败。