I have model method for method of controller.
How to test it with jest ?
How to create mock of Objection ?
const { Errors } = require('../../db/models/Errors');
module.exports = async function get(page = 0, perPage = 5, errorsFilter = {}) {
try {
const query = Errors
.query()
.orderBy('id', 'DESC')
.page(page, perPage);
// filter
if (Object.keys(errorsFilter).length) {
for (let field in errorsFilter) {
if ( errorsFilter.hasOwnProperty(field) ) {
if (field === 'dateStart') {
query.where('date', '>=', `${ errorsFilter[field] }`);
} else if (field === 'dateEnd') {
query.where('date', '<=', `${ errorsFilter[field] }`);
} else {
query.where(field, 'like', `%${ errorsFilter[field] }%`);
}
}
}
}
const { results, total } = await query;
return {
items: results,
itemsOnPage: Number(perPage),
currentPage: Number(page),
totalPage: Math.ceil(total/perPage),
totalItems: Number(total)
}
} catch (error) {
// log error -----------------------------------------------------------
console.log({Error: error});
// ---------------------------------------------------------------------
}
};
My test file:
require('dotenv').config();
const express = require('express');
const request = require('supertest');
const bodyParser = require('body-parser');
const { Objection } = require('objection');
const { QueryBuilder } = require('objection');
const { Errors } = require('../../../../server/db/models/Errors');
const errors = require('../../../../server/controllers/errors');
const { i18next, i18nextMiddleware } = require('../../../i18n-server');
const builder = QueryBuilder.forClass(Errors);
console.log(builder.resolve);
jest.spyOn(Errors, 'query').mockImplementation(builder.resolve({ type: 'test' }));
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(i18nextMiddleware);
app.get(
'/',
errors.getErrors
);
describe('get errors', () => {
it('success', done => {
i18next.on('initialized', () => {
request(app)
.get('/')
.query({page: 1, perPage: 5})
.end((err, res) => {
if (err) return done(err);
expect(res.status).toBe(200);
done();
});
})
});
});
I have error:
TypeError: specificMockImpl.apply is not a function
Change to
jest.spyOn(Errors, 'query').mockReturnValue(builder.resolve({ list: 'test' }));
I have error
Error: no database connection available for a query. You need to bind the model class or the query to a knex instance.
Add knex
const { knex } = require('../../../../server/db/connect');
const { QueryBuilder, Objection, Model } = require('objection');
Model.knex(knex);
Now still there is a database request. How to disable it?
The error occurs because Model.knex(knex); required function.
Without it, tests do not pass.
If I disconnect my database, then how do I simulate a database connection?
const { results, total } = await query;
results - my mock values
total - value from db query
How to apply the method ?
const modelUser = jest.spyOn(Users, 'query');
modelUser
.mockReturnValue(
builder.resolve([
{
"id": 91,
"login": "test_user",
"type": "manager",
"edit": 0,
"email": "[email protected]",
"phone": "79098160405",
"block": 1
},
{
"id": 77,
"login": "y475y74gwytgyuhbtwghrwhg874yt1",
"type": "manager",
"edit": 0,
"email": "[email protected]",
"phone": "76756655457",
"block": 0
}
])
);
jest.spyOn(modelUser, 'page').mockReturnValue({ total: 100 });
Error
Cannot spy the page property because it is not a function; undefined given instead
I mock query
jest.spyOn(Errors, 'query').mockReturnValue(
builder.resolve(
[
{ id: 1, name: 'Error 1' },
{ id: 2, name: 'Error 2' }
])
);
But page function is executed.
Result:
{ items: [ { id: 1, name: 'Error 1' }, { id: 2, name: 'Error 2' } ],
itemsOnPage: 5,
currentPage: 1,
totalPage: 24,
totalItems: 117 }
Where in the page method does the array with the results come from?
It is working. But maybe a better way?
jest.spyOn(Errors, 'query')
.mockReturnValue(
builder.resolve(errorsMockDB)
);
jest.spyOn(builder, 'page')
.mockImplementation(
(page, perPage) => {
return builder.resolve({
items: errorsMockDB.getErrors.splice(page * perPage, perPage),
total: errorsMockDB.getErrors.length
})
}
);
Why doesn't where work ?
It is working
require('dotenv').config();
const express = require('express');
const request = require('supertest');
const bodyParser = require('body-parser');
const { Model } = require('objection');
const { Users } = require('../../../../server/db/models/Users');
const users = require('../../../../server/controllers/users');
const checkFilter = require('../../../../server/middleware/checkFilter');
const { i18next, i18nextMiddleware } = require('../../../i18n-server');
const { knex } = require('../../../../server/db/connect');
const usersMockDB = require('../../../mock_db/users');
Model.knex(knex);
const builder = Users.query(knex);
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(i18nextMiddleware);
app.get(
'/',
checkFilter(['type']),
users.getUsers
);
jest.spyOn(Users, 'query')
.mockReturnValue(
builder.resolve(usersMockDB.getUsers)
);
describe('get errors', () => {
beforeAll(done => {
i18next.on('initialized', () => {
done()
});
});
it('without filter', done => {
request(app)
.get('/')
.query({page: 0, perPage: 5})
.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.itemsOnPage).toBe(5);
expect(res.body.currentPage).toBe(0);
expect(res.body.totalPage).toBe(Math.ceil(usersMockDB.getUsers.length/5));
expect(res.body.totalItems).toBe(usersMockDB.getUsers.length);
console.log(res.body);
done();
});
});
afterAll(done => {
knex.destroy();
done();
})
});
But value of totalItems from server ...
I created this function, see if it helps
import { QueryBuilder } from 'objection'
export default function mockmodel(Model) {
const query = QueryBuilder.forClass(Model)
jest.spyOn(Model, 'query').mockReturnValue(query)
return query
}
const body = { test : 1 }
const queryBuilderMock = mockModel(YourModel).resolve(body)
YourModel.query.insert({})
// if needed
const insertMock = jest.spyOn(queryBuilderMock, 'insert')
expect(insertMock).toHaveBeenCalledTimes(1)
I'm not sure if you should really be mocking out the database. I would consider any test that runs against Objection models to be an integration test, which should be backed by a real database.
If you really want to mock out the response, you could look at the resolve and reject Query Builder methods, although I'm unsure whether you can skip a database connection if using these.
As an aside, you might have more luck posting this sort of question on the gitter channel.
@fiznool said it all :+1:
Most helpful comment
I'm not sure if you should really be mocking out the database. I would consider any test that runs against Objection models to be an integration test, which should be backed by a real database.
If you really want to mock out the response, you could look at the resolve and reject Query Builder methods, although I'm unsure whether you can skip a database connection if using these.
As an aside, you might have more luck posting this sort of question on the gitter channel.