Unable to unit test firebase function in Sinon - javascript

I am trying to unit test a JavaScript promise, that contains a firebase query, using mocha, chai, sinon. I am trying to mock the database using sinon rather than actually making a request to the database. However, I am unable to implement it correctly.
Here is my promise in the file '/services/pr_services:
exports.getUserInfo = (userId) => {
return new Promise((resolve, reject) => {
const userProfile = {};
const userProfileRef = database.ref('profiles').child(userId);
userProfileRef.once('value', (snap) => {
if (snap.exists()) {
const userProfileData = snap.val();
resolve(userProfile);
} else {
reject();
}
});
});
};
The variable database contains the database configuration like credentials, database url, etc
Here is my code for the test case:
const chai = require('chai');
const sinon = require('sinon');
const admin = require('firebase-admin');
const database = require('../database');
const services = require('../services/pr_services');
const should = chai.should();
describe('Database functions', () => {
let adminInitStub;
before(() => {
adminInitStub = sinon.stub(admin, 'initializeApp');
});
describe('get profile info', () => {
it('should return a non empty object', (done) => {
beforeEach(() => {
services.getUserInfo = sinon.stub();
});
afterEach(() => {
services.getUserInfo.reset();
});
const userId = 'jim123';
const snap = {
name: 'Jim Dani',
address: 'Porto'
};
const userProfileRef = database.ref('profiles').child(userId);
userProfileRef.once('value').returns(Promise.resolve(snap));
services.getUserInfo
.then(info => {
info.should.be.a('object');
info.should.equal(snap);
done();
})
.catch(err => {
should.not.exist(err);
done();
});
});
});
after(() => {
adminInitStub.restore();
test.cleanup();
});
});
Can anyone point out where I am going wrong and kindly point me in right direction.
Thanks.

Related

Jest mock module.exports module and count called times

I have some dependency in the tested module.
sendResponse.js:
module.exports = function sendResponse(res, data) {
res.send(data);
};
testedModule.js:
const login = require('.../sendResponse');
exports.postLogin = withServerErrorHandler(async (req, res) => {
const { body } = req;
const { email, password } = body;
const user = await login(email, password);
return sendResponse(res, user);
});
And I want to mock sendResponse module and count its calls.
const testedModule = require('.../testedModule');
jest.mock('.../sendResponse', () => jest.fn());
const sendResponse = require('.../sendResponse');
describe('testedModule', () => {
const res = {
//..
};
const req = {
//..
};
describe('Authentication', () => {
test('should pass', async (done) => {
await testedModule.postLogin(req, res);
expect(sendResponse).toHaveBeenCalledTimes(1);
done();
});
})
});
Unfortunately, Jest always gives me that result, It looks like Jest jest spy on jest.fn() function, not specific ref. How can I deal with it?
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0

Mock sequelize with Jest and sequelize-mock

I'm building a new project and I'm trying to use TDD as my default methodology and trying to apply it with integration test.
The thing is easy I want to retrieve all the users from my DB.
//controller.js
const UserService = require('../services/user');
module.exports = {
// corresponds to /users
getAllUsers: async (req, res, next) => {
const users = await UserService.fetchAllUsers();
res.send(users);
},
};
const models = require('../database/models/index'); // I tried also with destructuring
module.exports = {
fetchAllUsers: async () => models.user.findAll(),
};
and my actual test file looks like
const request = require('supertest');
const SequelizeMock = require('sequelize-mock');
const app = require('..');
const dbMock = new SequelizeMock();
const models = require('../src/database/models/index');
jest.mock('../src/database/models/index', () => ({
user: jest.fn(),
}));
models.user.mockImplementation(() => {
const UserMock = dbMock.define('user', {});
UserMock.$queueResult(UserMock.build({
username: 'username',
mail: 'myMail#myMail.com',
}));
return UserMock;
});
describe('Demo test', () => {
it('should respond to users route', (done) => {
request(app)
.get('/users')
.end((err, res) => {
expect(err).toBeNull();
expect(res.status).toBe(200);
expect(res.json).toBe('object');
done();
});
});
});
All of this is actually working but when I'm trying to mock the user I TypeError: models.user.findAll is not a function
I need to replace the model.user with the UserMock's value.
Is there anyway to do this? or I should just mock findAll like
jest.mock('../src/database/models/index', () => ({
user: () => ({ findAll: jest.fn() }),
}));
``
In this case, I was not mocking what I wanted to.
If you wish to mock the models all you need to do is use SpyOn
like
const { sequelize, Report, User } = require('../../database/models/index');
describe("my test", () => {
it("should just mock", () => {
jest.SpyOn(Report, "count").mockResolvedOnce(6);
})
})
In the case that you need to mock like your service
first create jest functions to allow the access from outside the scope of your mock
and then mock it with "double class call"
const BaseService = require('../BaseService');
const mockFecthOne = jest.fn();
jest.mock('../BaseService',
() => jest.fn().mockImplementation(
() => ({
fetchOne: mockFetchOne,
}),
));
// inside your test just
BaseService().fetchOne.mockResolvedOnce({....})
and thats it hope it works.

Is it possible to mock API calls with Jest in NodeJS without jest.mock('module')?

My NodeJS application has to do some API requests, so I'm mocking their return as my tests are just for my application's business logic. However, there's two things that I quite didn't understand.
I'm using jest's mockImplementation method to change the return of my service, but I can't make it work without calling jest.mock with the service beforehand.
Also, if I try to set automock: true in my jest.config.js, it returns me an error:|
TypeError: Cannot set property 'gracefulify' of undefined
Here's my test.js code in where I'm testing a function that calls automation.js, which has my application logic and make the calls for my services:
const automation = require('../automations/fake.automation');
// MOCKS
const mockedBlingProduct = require('../mocks/bling-product.mocks.json');
const mockedShopifyCreatedProduct = require('../mocks/shopify-created-product.mocks.json');
// SERVICES
const BlingProductService = require('../services/bling-product.service');
const ShopifyProductService = require('../services/shopify-product.service');
jest.mock('../services/bling-product.service');
jest.mock('../services/shopify-product.service');
describe('Automation test', () => {
beforeEach(() => {
const blingMockedReturn = jest.fn(() => {
return mockedBlingProduct;
});
const shopifyMockedReturn = jest.fn(() => {
return mockedShopifyCreatedProduct;
});
BlingProductService.mockImplementation(() => {
return {
list: blingMockedReturn
};
});
ShopifyProductService.mockImplementation(() => {
return {
create: shopifyMockedReturn
};
});
});
it('should return status SUCCESS', async () => {
const result = await
.run();
expect(result).toEqual({ status: 'SUCCESS' });
});
});
And here's the code of one of my services, keep in mind that the logic behind the API calls is abstracted from the service. In the mockImplementation I'm trying to overwrite the list and create functions inside them:
class BlingPriceService {
async list(query = {}) {
const httpMethod = 'GET';
const resource = 'produtos/page={pageNumber}/json';
const options = {
queryString: query,
urlParams: {
pageNumber: 1,
}
};
return blingComponent.request(httpMethod, resource, options);
}
}
module.exports = BlingPriceService;
const automation = require('../automations/fake.automation');
// MOCKS
const mockedBlingProduct = require('../mocks/bling-product.mocks.json');
const mockedShopifyCreatedProduct = require('../mocks/shopify-created-product.mocks.json');
// SERVICES
const BlingProductService = require('../services/bling-product.service');
const ShopifyProductService = require('../services/shopify-product.service');
describe('Automation test', () => {
beforeAll(() => {
jest.spyOn(BlingProductService.prototype, 'list').mockImplementation(() => Promise.resolve(mockedBlingProduct));
jest.spyOn(ShopifyProductService.prototype, 'list').mockImplementation(() => Promise.resolve(mockedShopifyCreatedProduct));
});
afterAll(() => {
jest.restoreAllMocks();
});
it('should return status SUCCESS', async () => {
const result = await automation.run();
expect(result).toEqual({ status: 'SUCCESS' });
});
});

JEST Mocking from node modules

I have been trying this for a while and i couldn't find the solution.
I tried a lot of solutions (__ mock __ folder, mockimplementation, mock and more) but i always had the same error client.mockImplementation is not a function
//restclient.js
module.exports = (cfg) => new Client(config);
// api.js
const client = require('restclient');
module.exports.doRequest = () => {
const request = client();
const config = {};
request.get('/path/to/request', config)
.then(result => console.log(result))
}
//api.tests.js
const client = require('restclient');
const api = require('./api');
jest.mock('restclient', () => () => ({
get: jest.fn(),
}));
describe('testing API', () => {
test('test then', async () => {
try {
restclient.mockImplementation(() => () => ({
get: (url, config) => 'Hi! I\'m mocked',
}));
const result = await api.doRequest();
console.log('result', result);
} catch (e) {
console.log('eee', e);
}
});
});
I could not found the solution, I think I can't mock the const request = restclient() part but i dont know why!!
You were missing to mock the constructor.
This mocks the constructor of restclient
jest.mock('restclient', ()=> jest.fn().mockImplementation(() => {
/** here you can create and return a mock of request **/
}));

Node.js - unit test for function with mocked promise inside

I'm currently making a small server in JavaScript and as part of the learning process I'm writing unit tests for the functions. Unfortunately I ran into major difficulties with a certain test that handles a promise. Below is the router module, with a separate handlePUT function for ease of testing.
const express = require('express');
const service = require('../service/user.service');
const dutyStatusRouter = express.Router();
const output = console;
function handlePUT(req, res) {
service.updateUserStatus()
.then((fulfilled) => {
res.status(fulfilled);
res.send();
})
.catch(() => {
res.status(500);
res.send();
});
}
dutyStatusRouter.route('/').put(handlePUT);
The updateUserStatus function basically toggles a Boolean in a database and looks somewhat like this:
function updateUserStatus() {
return new Promise((resolve, reject) => {
if (…) {
resolve(201);
} else if (…) {
resolve(200);
} else {
reject();
}
});
}
As for the unit tests, I'm using mocha/chai, with proxyquire to create a mock updateUserStatus.
const chai = require('chai');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
const serviceStub = {};
describe('=== Unit test ===', () => {
it('Handle PUT test: promise kept', async () => {
const dutyStatusRouter = proxyquire('../../router/duty-status.router', {
'../service/user.service': serviceStub,
});
serviceStub.updateUserStatus = () => {
return new Promise((resolve, reject) => {
resolve(200);
});
};
const res = {
status: sinon.fake(),
send: sinon.fake(),
};
await dutyStatusRouter.handlePUT({}, res);
chai.assert(res.status.calledOnceWith(200));
});
});
Whenever I try to run the unit test, I get the error Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.. If I try to add done() it still fails by giving the error message Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
Found a solution that works, so I'm adding it here:
const chai = require('chai');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
const serviceStub = {};
const dutyStatusRouter = proxyquire('../../router/duty-status.router', {
'../service/user.service': serviceStub,
});
describe('=== Unit test ===', () => {
it('Handle PUT test: promise kept', (done) => {
serviceStub.updateUserStatus = sinon.stub().resolves(200);
const res = {
status: sinon.fake(),
send: sinon.fake(),
};
dutyStatusRouter.handlePUT({}, res).then(() => {
chai.assert(res.status.calledWith(200));
done();
});
});
});
Note: I changed the handlePUT function just a bit, it now looks like this (I just added a return):
function handlePUT(req, res) {
return service.updateUserStatus()
.then((fulfilled) => {
output.log('Promise fulfilled');
res.status(fulfilled);
res.send();
})
.catch(() => {
output.log('Promise unfulfilled');
res.status(500);
res.send();
});
}

Categories