Vincit / objection.js

An SQL-friendly ORM for Node.js
https://vincit.github.io/objection.js
MIT License
7.24k stars 635 forks source link

How do you mock? #1477

Closed DHFW closed 4 years ago

DHFW commented 4 years ago

For unit testing and mocking of database calls I use the following:

modelsSpy = jest.spyOn(models.CourseParticipation, 'query');
      modelsSpy.mockReturnValue({
        eager: (): any => {
          return {
            findById: (queryObject: any): Partial<CourseParticipation> => {
              return {
                PersonID: 1,
                SomeOtherKeyID: 1,
                Course: {
                  CourseCode: '1234',
                  SpecialtyID: 123,
                  Title: 'Test course',
                  Sessions: [
                    {
                      Date: new Date(2019, 0, 1), // Local time
                      Begintime: new Date(1970, 0, 1, 13, 0, 0, 0), // Local time
                    },
                  ],
                },
              };
            },
          };
        },
        where: (): any => {
          return {
            patch: (): number => {
              return 1;
            },
          };
        },
      });

This works pretty well for me (ObjectionJS version: 1.6.8) with Jest 24. I wonder what your code looks like? We don't have so much help from TypeScript here (using everything as any because the models have so many properties and methods), so any ;) suggestions are welcome!

koskimas commented 4 years ago

You can use the resolve method to skip the db query and mock the db result.

DHFW commented 4 years ago

@koskimas Thanks for the quick response. However, do you have an example how to implement this in a simple unit test? How do I go about this when I want to spy/mock on just say one findById request?

will-hang commented 4 years ago

Yeah, I'd love to +1 @DHFW's most recent comment. Specifically,

if I already have calls to Model.query().select().join() in my server code, is there any cleaner way to mock the entire call in some unit test? Or will @DHFW's first post have to suffice?

devinivy commented 4 years ago

You can override the model's query() method so that it returns a query builder calling resolve() with whatever value you like. There's some more info about this in the docs here: https://vincit.github.io/objection.js/recipes/custom-query-builder.html#custom-query-builder-extending-the-query-builder

will-hang commented 4 years ago

Thanks @devinivy. Does this mean that I either need to go and edit my DB code so that it calls the resolve() function? As in, there is no in-place mock? Or, are you suggesting that I can completely mock out query() with resolve() and everything will be fine?

The reason I'd like to clarify is that query() is often the first in a chain of calls. Mocking query() to return the result of resolve() instead, when there are still chained calls after query(), seems like it will break anything chained onto query().

devinivy commented 4 years ago

It should work just fine without modifying any application code! As the docs show, queryBuilder.resolve() returns queryBuilder, so you can keep chaining :) https://vincit.github.io/objection.js/api/query-builder/other-methods.html#resolve

will-hang commented 4 years ago

Oh damn! Amazing! Thanks so much for pointing that out. This is a huge plus for the community because this just makes server testing so much easier now. Thank you all!

aalimovs commented 4 years ago

For anyone stumbling upon this from hapi/hapipal/schwifty land, here's an example for a route handler:

// route handler
const { Person } = request.models(true);
const person = await Person.query().findById(request.params.id);
// test with stub
import { QueryBuilder } from 'objection';

const { Person } = server.models(true); // or import Person from '../wherever/models/Person';

Sinon.stub(server.models(true), 'Person').value({
    query: () => QueryBuilder.forClass(Person).resolve({ id: 111 }),
});
lucasrcosta commented 3 years ago

I have found that mock-knex offers the most complete experience for mocking and asserting the queries. :orange_circle: :orange_circle:

richardweaver commented 3 years ago

@lucasrcosta How are you injecting the mock-knex instance? I can't figure it out.