Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Testing with Jest and Jest.Mock #5014

Closed
seawatts opened this issue Dec 2, 2019 · 16 comments · Fixed by prisma/docs#1584
Closed

Docs: Testing with Jest and Jest.Mock #5014

seawatts opened this issue Dec 2, 2019 · 16 comments · Fixed by prisma/docs#1584
Assignees
Labels
domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. topic: tests

Comments

@seawatts
Copy link

seawatts commented Dec 2, 2019

I am trying to figure out a good pattern for using Jest with Photon. If anyone has suggestions it would be great. And Would probably be good to document this somewhere as well.

I have tried out a couple different things but they don't seem to be working with the jest.mock function. Every time I try to run jest.mock('@prisma/photon') it doesn't actually mock out the instance that has been created.

@pantharshit00
Copy link
Contributor

We had some test utils that are not maintained anymore since we are focusing on the core right now: https://github.com/prisma/prisma-test-utils

They will give you a vague idea on what testing could look like with photon in the future. But we do need to create some testing guides

@seawatts
Copy link
Author

seawatts commented Dec 3, 2019

Thanks @pantharshit00 I had taken a look at those but had a hard time using those patterns in my own tests for some reason.

@timsuchanek
Copy link
Contributor

Thanks for asking @seawatts!
We need to further explore mocking with Photon, we for sure should write content about that.

One thing, which is already very useful, is changing the datasource, that Photon connects to in the Photon constructor.

You can e.g. say:

const photon = new Photon({
  datasources: {
    db: 'file:test.db'
  }
})

if you just want to use a local test db.

@seawatts
Copy link
Author

@timsuchanek thanks, that is helpful. However, isn't very useful when trying to do a lot of unit tests.

@matthewmueller
Copy link
Contributor

matthewmueller commented Dec 19, 2019

I'm not super familiar with Jest, so I wasn't able to get it fully working, but I have setup how you should mock Photon and I have an idea on how to make Photon more mockable.

schema.prisma

generator photon {
  provider = "photonjs"
}

model User {
  id         Int      @id
  name       String
  posts      Post[]
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
}

model Post {
  id         Int      @id
  title      String
  author     User
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
}

index.ts

Simple API that allows you to get users from Photon.

import { Photon } from '@prisma/photon'
import express from 'express'

export default class API {
  public readonly app: express.Express

  constructor(private readonly photon: Photon) {
    this.app = express()
    this.app.get('/', this.index.bind(this))
    this.app.get('/users', this.getUsers.bind(this))
  }

  index(_req: express.Request, res: express.Response) {
    res.send('welcome to this wonderful server')
  }

  getUsers(
    _req: express.Request,
    res: express.Response,
    next: express.NextFunction
  ) {
    this.photon.users
      .findMany()
      .then(users => res.send(users))
      .catch(next)
  }

  listen(port: number, handler: (args: any[]) => void) {
    this.app.listen(port, handler)
  }
}

index.test.ts

import { Photon } from '@prisma/photon'
import supertest from 'supertest'
import API from './index'

jest.mock('@prisma/photon')

describe('API', () => {
  it('GET /users', async () => {
    const photon = new Photon()
    const api = new API(photon)
    await supertest(api.app)
      .get('/users')
      .expect(200)
    expect(photon.users).toBeCalled()
  })
})

I got stuck at the jest.mock("@prisma/photon") auto-mocking magic, so this test doesn't pass yet.


To make mocking more "traditional", I suggest we export a Photon interface that can be mocked:

interface IPhoton {
    connect(): Promise<void>;
    disconnect(): Promise<void>;
    get users(): UserDelegate;
    get posts(): PostDelegate;
}

Then to implement it you could have something like the following (I didn't test this):

class PhotonMock implements IPhoton {
  public connect: () => Promise<void>
  public disconnect: () => Promise<void>
  public get users: () => UserDelegate
  public get posts: () => PostDelegate
  constructor(photon: Photon) {
    this.connect = photon.connect
    this.disconnect = photon.disconnect
    this.users = photon.users
    this.posts = photon.posts
  }
}

You can then granularly mock what you want to mock, and passthrough the rest:

import { Photon, UserDelegate } from '@prisma/photon' 

const mock = new PhotonMock(Photon())
mock.users = () => {
  // return mocked implementation
} 

This is quite tedious so we'll probably want to autogenerate this mockable photon class.

@timsuchanek
Copy link
Contributor

timsuchanek commented Jan 14, 2020

We recommend testing against a real test database for now: https://github.com/prisma/photonjs/issues/317#issuecomment-561558330

In the future a mocking api could look like this:

const photon = new Photon({
  mocks: {
    users: [{
      findMany: {
        args: { where: {} },
        response: []
      }
    }]
  }
})

@seawatts
Copy link
Author

seawatts commented Jan 15, 2020

I ended up doing something different

import { Photon } from '@prisma/photon';

const modelNames = ['user', 'tenant'];
const modelFunctions = ['findOne', 'findMany', 'create', 'delete', 'update', 'deleteMany', 'updateMany', 'upsert', 'count'];

const mock = modelNames.reduce((obj, modelName) => {
  const objCopy = {
    ...obj,
  };

  objCopy[modelName] = jest.fn();

  modelFunctions.forEach(modelFunction => {
    objCopy[modelName][modelFunction] = jest.fn();
  });

  return objCopy;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
}, {} as any) as jest.Mocked<Photon>;

I don't love it but it works

Because this allows me to do the following

import { Photon } from '@prisma/photon';
import { mocked } from 'ts-jest/utils';

jest.mock('@photon/prisma');
const mockDb = mocked(db, true);

const mockUser = { id: '1'};
const mockTenant = {id: '2'};
const mockCreatedBy = jest.fn().mockResolvedValue(mockUser);
mockDb.tenants.findOne.mockImplementation((): any => {
  return {
    createdBy: mockCreatedBy,
  };
});


it('tests the db', async () => {
  mockDb.tenants.findMany.mockResolvedValue([mockTenant]);

  await myFunctionThatCallsTenantsFindMany()

  expect(mockDb.tenants.findMany).toBeCalled();

});

@BenJeau
Copy link

BenJeau commented Jun 26, 2020

Just curious, is there a timeline for testing with the prisma client (since prisma v2 got out of beta)?

@matthewmueller
Copy link
Contributor

matthewmueller commented Sep 8, 2020

Hey everyone, we'd love to learn more about why you're trying to mock the Prisma Client.

We currently suggest running your tests against a real test database. From my perspective, installing and running a test database locally is straightforward nowadays and it's better to try and simulate your production environment as much as possible.

Do you have any use cases where this would not be possible or practical?

@Sytten
Copy link
Contributor

Sytten commented Sep 8, 2020

We personally do unit tests with a mocked version of prisma for faster local iteration. We use https://github.com/marchaos/jest-mock-extended for that. We also have test with a real database, but there prisma could help us to seed the DB at the beginning and delete the data at the end of the test suite or specific test. Currently we have a custom script with a list of tables to truncate and a list of input to seed, but it is not ideal and not easy to seed different data for each test suite.

@matthewmueller
Copy link
Contributor

matthewmueller commented Sep 8, 2020

@Sytten thanks for sharing. Faster feedback is an interesting point. What's your test setup look like with https://github.com/marchaos/jest-mock-extended?

We've played around with the idea of using a pool of docker containers to run tests in parallel or at least have warm databases ready at the end of each test. I could see that being an acceptable solution to the faster iteration problem.

Yah, we also really need to make it easier to seed. That seems like a more urgent need to me. I'd expect seeding to come shortly after migrate stabilizes.

@Sytten
Copy link
Contributor

Sytten commented Sep 8, 2020

Basically all our core logic function take in a context parameter that is usually the context passed from apollo. But in the case of the unit test I would create a fake context with a mocked version of prisma. What is super great with this lib is that you get full typing of the functions you mock so I do something like:

mockCtx.prisma.ticketTier.findMany.mockResolvedValue([
      givenTicketTier(1),
      givenTicketTier(2),
    ]);

And it will tell you if you return a bad object. Obviously it doesn't work if you start to include/select children fields.

Yeah having a way to spawn multiple DB, seed them, direct the client to it and run the test would be great for unit testing with a live DB. But even just basic seeding tooling would help a lot.

@pantharshit00 pantharshit00 transferred this issue from prisma/prisma-client-js Jan 13, 2021
@pantharshit00 pantharshit00 added kind/docs kind/feature A request for a new feature. domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. labels Jan 13, 2021
@molebox
Copy link

molebox commented May 7, 2021

Hi folks, this has been addressed in prisma/docs#1664 so im going to go ahead and close it. If you have anything further to add please create a new issue. 🙌

@molebox molebox closed this as completed May 7, 2021
@janpio janpio added topic: tests and removed kind/feature A request for a new feature. labels May 7, 2021
@matthewmueller
Copy link
Contributor

The documentation is live here: https://www.prisma.io/docs/guides/testing/unit-testing.

Thanks @molebox!

@huv1k
Copy link

huv1k commented Jun 2, 2023

It would be great to have something more complex for example with relations 🙌 There is no good DX regarding this 😢

@janpio
Copy link
Contributor

janpio commented Jun 7, 2023

Can you open an separate issue for that @huv1k? Then we can track this and try to make it happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. topic: tests
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants