Kay
Kay

Reputation: 19680

How to mock prisma with jest-mock

I use prisma to interact with my database and i would like to use jest-mock to mock the findMany call. https://jestjs.io/docs/jest-object#jestmockedtitem-t-deep--false

brands.test.ts

import { PrismaService } from "@services/mysql.service";
import { mocked } from "jest-mock";
import faker from "@faker-js/faker";
import { GetBrands } from "./brand";
jest.mock("@services/mysql.service");

/**
 * @group unit
 */

describe("Brand", () => {
    afterAll(async () => {});

    const mockedPrismaService = mocked(PrismaService, true);
    it("should get a list of brands", async () => {
        const mockedData = [
            {
                id: faker.datatype.uuid(),
                name: faker.datatype.string(),
                image: {
                    source: "some_source",
                    dtype: "some_dtype",
                },
            },
        ];

        //@ts-ignore - because of relational data mockedData.image
        mockedPrismaService.brand.findMany.mockResolvedValueOnce(mockedData);

        const [response, error] = await GetBrands();

        console.log(response, error);
    });
});

mysql.service.ts

import mysql from "mysql2/promise";
import { Config } from "@config";
import { PrismaClient, Prisma } from "@prisma/client";

export const MySQLEscape = mysql.escape;
export const MySQLPreparedStatement = mysql.format;

export const PrismaService = new PrismaClient({});
export const PrismaHelper = Prisma;

However when i run this test i get the following error.

TypeError: Cannot read properties of undefined (reading 'brand')

Upvotes: 4

Views: 12937

Answers (3)

sefiks
sefiks

Reputation: 1650

I added prisma object as an optional input to my resolver. If it is not being sent from parent class, it is generated in the runtime. But if it is being sent, then it is going to be used. So, I am not sending anything to that resolver in runtime but for testing purposes i am sending a MockPrismaClient class.

This is the design of my resolver

import { PrismaClient } from '@prisma/client';

export class MyResolver{
  private prisma: any;

  constructor(prisma?: any) {
    this.prisma = (prisma || new PrismaClient()) as PrismaClient;
  }

  async perform(customerName: string, customerSurname: string) {
     const result = await this.prisma.Customers.create({
        data: {
           name: customerName,
           surname: customerSurname
        }
     })
  }

And this is my test file

import {MyResolver} from '../src/MyResolver'

class MockPrismaClient {
    Customers = {
       create: jest.fn(),
    };
}

describe('CustomOnboardingResolver', () => {
   it('mocking prisma client', async () => {
      const mockPrisma = new MockPrismaClient();

      // set a mock return value for Customers.create
      mockPrisma.Customers.create.mockResolvedValueOnce({customerId: 123});

      const resolver = MyResolver(mockPrisma);
      await resolver.perform("John", "Casey")

      expect(mockPrisma.Customers.create).toHaveBeenCalledWith({
        data: {
          name: "John",
          surname: "Casey"
        },
      });  
     }
}

I hope this approach would help you.

Upvotes: 0

Oleksandr Hrin
Oleksandr Hrin

Reputation: 886

example from my experience, in brands.test.ts:

import { PrismaService } from "@services/mysql.service";
..
jest.spyOn(PrismaService.brand , 'findMany').mockResolvedValueOnce(mockedData)

Upvotes: 0

devklick
devklick

Reputation: 2629

Factory Mock

One option is to option use the factory approach when mocking your client.

jest.mock("@services/mysql.service", () => ({
    PrismaService: {
        brand: {
            findMany: jest.fn(() => { })
        }
    },
}));

Then within your test, you can mock the findMany function to return your test data, then call the function being tested.

const mockedData = [...];
PrismaService.brand.findMany.mockResolvedValueOnce(mockedData);
const result = await GetBrands();

It's a bit cumbersome, but it works.

Note that in my example, I've implemented GetBrands as follows:

import { PrismaService } from "@services/mysql.service"

export const GetBrands = async () => {
    const data = await PrismaService.brand.findMany();
    return data;
}

Your example

In your example, you're using automatic mocking, and I'm not too familiar with it so I'm not sure how to get it working.

What seems to be happening to cause the error is your PrismaService is undefined when it's imported here:

import { PrismaService } from "@services/mysql.service";

And then calling the mocked function with an undefined parameter returns undefined:

const mockedPrismaService = mocked(undefined, true); // returns undefined

And finally, calling the following is what throws the error:

mockedPrismaService.brand.findMany.mockResolvedValueOnce(mockedData);
// TypeError: Cannot read properties of undefined (reading 'brand')

I would have thought something like this would be what you're after, but this throws an error:

jest.mock("@services/mysql.service", () => ({
    PrismaService: mocked(PrismaService, true)
}));
//    6 |
//    7 | jest.mock("@services/mysql.service", () => ({
//>   8 |     PrismaService: mocked(PrismaClient, true)
//      |                    ^
//    9 | }));

Check out the docs

Might be worth checking out the Prismas documentation on unit testing, as they suggest a couple of pretty different approaches.

Upvotes: 2

Related Questions