11

Most examples I've seen of how to test a Prisma-injected NestJS Service (e.g. prisma-sample in testing-nestjs) are for "end to end" testing. They actually access the database, performing actual queries and then rolling back the results if necessary.

For my current needs, I want to implement lower-level "integration" testing.

As part of this, I want to remove Prisma from the equation. I want the focus to be on my service's functionality instead of the state of data within the database and Prisma's ability to return it.

One big win of this approach is that it obviates the need to craft "setup" queries and "teardown"/reset operations for specific tests. Instead, I'd like to simply manually specify what we would expect Prisma to return.

In an environment consisting of NestJS, Prisma, and Jest, how should I accomplish this?


UPDATE: The author of the testing-nestjs project pointed out in the comments that the project does have an example of database mocking. It looks nice! Others may still be interested in checking out the Gist that I've linked to as it includes some other useful functionality.

rinogo
  • 8,491
  • 12
  • 61
  • 102
  • As you can see, I've added my own answer already. I'm curious what other approaches others prefer. – rinogo Dec 04 '21 at 19:40
  • [By the way, that same `testing-nestjs` repo you linked, it has unit tests for prisma where the database _is_ mocked](https://github.com/jmcdo29/testing-nestjs/blob/master/apps/prisma-sample/src/cat/cat.service.spec.ts) (source, I'm the author) – Jay McDoniel Dec 05 '21 at 17:38
  • @JayMcDoniel I thought you’d see this! Can you share a link? All I remember seeing were tests that seemed to be perform actual queries on the DB. Maybe I just misunderstood. – rinogo Dec 05 '21 at 18:18
  • I was on mobile and didn't see the link - I'll check it out! – rinogo Dec 05 '21 at 18:19
  • Ahhhh I see now - I thought the repo only had e2e tests. This is great; thanks, Jay! – rinogo Dec 05 '21 at 18:20

3 Answers3

14

To get a reference to your service's prisma instance, use:

prisma = module.get<PrismaService>(PrismaService)

Then, assuming your function calls prisma.name.findMany(), you can use jest.fn().mockReturnValueOnce() to mock (manually specify) Prisma's next return value:

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

(Of course, you would change prisma.name.findMany in the code above to match whatever function you're calling.)

Then, call the function on your Service that you're testing. For example:

expect(await service.getFirstJob("steve")).toBe('developer');

That's it! A full code example can be found here.

rinogo
  • 8,491
  • 12
  • 61
  • 102
9

You can also mock the whole Prisma client by using the jest-mock-extended package, which is the approach used in the official Prisma documentation.

Assuming you have created a PrismaService as suggested in the NestJS docs, the test would be:

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);
  });
});

One benefit of this approach is that you can easily mock on the Prisma client using the Jest Mock functions API.

David Ferreira
  • 1,233
  • 17
  • 24
  • I'd prefer this way of doing it since it's the one recommended by the official docs. Thank you for providing the Nest example! – Pierre C. Apr 01 '23 at 17:29
6

Nest.js offers an API for mocking whole services, and Prisma is just one of them.

For simplicity, let's assume we access prisma directly from app controller.

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();
  }
}

Then the test would be:

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 is the following file (as suggested here 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();
    });
  }
}

Be sure to use the instance of DbService, not string 'DbService'. Otherwise a DB call is still made and the test fails.

Ilya Kushlianski
  • 780
  • 9
  • 15