How to test objection.js with jest?
Asked Answered
A

2

9
Model.knex(knex);

const app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use(i18nextMiddleware);

I want to test method getUsers of users controller. It get data from usersModel getUsers method. I get date from MySQL by Objections ORM.

app.get(
  '/',
  checkFilter(['type']),
  users.getUsers
);

According to instructions, I am changing the query method.

class MyQueryBuilder extends QueryBuilder {
  query() {
    return this.resolve({ test: 11111 });
  }
}

class UsersModel extends Model {
  static get QueryBuilder() {
    return MyQueryBuilder;
  }
}

jest.spyOn(Users, 'query')
  .mockImplementation(UsersModel.query);

Describe test.

describe('get errors', () => {
  beforeAll(done => {
    i18next.on('initialized', () => {
      done()
    });
  });

  it('with filter', done => {
    request(app)
      .get('/')
      .query({page: 0, perPage: 5, type: 'admin'})
      .end((err, res) => {
        if (err) return done(err);

        expect(res.status).toBe(200);
        expect(
          Object.keys(res.body).sort()
        ).toEqual([
          'items',
          'itemsOnPage',
          'currentPage',
          'totalPage',
          'totalItems'
        ].sort());
        expect(res.body.items).toHaveLength(8);
        expect(res.body.totalItems).toBe(usersMockDB.getUsers.length);

        console.log(res.body);

        done();
      });
  });

  afterAll(done => {
    knex.destroy();

    done();
  })
});

Method getUsers of users model.

    const { Users } = require('../../db/models/Users');

    const query = Users
      .query()
      .select(
        'id',
        'login',
        'type',
        'edit',
        'email',
        'phone',
        'block'
      )
      .orderBy('id', 'DESC')
      .page(page, perPage);

    // filter

    if (Object.keys(usersFilter).length) {
      for (let field in usersFilter) {
        if ( usersFilter.hasOwnProperty(field) ) {
          query.where(field, 'like', `%${ usersFilter[field] }%`);
        }
      }
    }

    const { results, total } = await query;

    return {
      items:       results,
      itemsOnPage: Number(perPage),
      currentPage: Number(page),
      totalPage:   Math.ceil(total/perPage),
      totalItems:  Number(total)
    }

Should I override methods page and where ? As I understand it, they make new database queries.

Attend answered 19/3, 2020 at 12:26 Comment(0)
R
7

This may not be desirable in every case, but I find that the easiest solution for unit tests that use objection models is to create one transaction per test. This does mean you'll need a database to run tests, but everything is rolled back between tests.

In jest.config.js, add this line

setupFilesAfterEnv: ['./jest.setup.js'],

in jest.setup.js:

import objection from 'objection';
import knex from './src/db/index.js'; // Change src/db/index.js to the path to the file where you export your knex instance

const {transaction, Model} = objection;

global.beforeAll(async () => {
    global.knex = knex;
    global.txn = null;
});

global.beforeEach(async () => {
    global.txn = await transaction.start(knex);
    Model.knex(global.txn);
});

global.afterEach(async () => {
    await global.txn.rollback();
    Model.knex(knex);
});

global.afterAll(async () => {
    global.knex.destroy();
});

You can then use your models as expected in your code and unit tests

import {User} from './src/db/models/index.js';

it('creates and reads users', async () => {
    const user = await User.query().insert({email: '[email protected]'});
    const users = await User.query();

    expect(users).toHaveLength(1);
});

it('does not persist users between tests', async () => {
    const users = await User.query();

    expect(users).toHaveLength(1);
});
Rhine answered 13/6, 2021 at 17:42 Comment(0)
P
0

This is working fine without knex and db connection.

role.js

const { Model } = require('objection'); 
const table = 'roles';

class Role extends Model { 
    static get tableName() {
        return 'roles';
    }
}

module.exports = Role;

model.test.js

const Role = require("../../models/role");

describe("db.createRole", () => {
    const queryMock = jest.spyOn(Role, "query");

    const rolePayload = {
        name: "admin",
    };

    const mockFunc = jest.fn();
    mockFunc.mockResolvedValue(rolePayload);

    queryMock.mockImplementation(() => {
        return {
            insert: mockFunc,
            insertAndFetch: mockFunc,
        };
    });

    it("calls.role.query()", async () => {
        const result = await Role.query().insert(rolePayload); // THIS WORKS WITHOUT DB CONNECTION
        const result2 = await Role.query().insertAndFetch(rolePayload); // THIS DOES NOT WORK WITHOUT DB CONNECTION
        expect(Role.query).toBeCalled();
    });
});

Output

PASS  tests/pocs/model.test.js
 db.createRole
  √ calls.role.query() (2 ms)
Pungent answered 1/10, 2023 at 4:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.