I have written a web service that indeed consumes an external web service, after performing some validations and data manipulations (plain business logic). I need to write unit tests for the same and need to have code coverage report as well. Many suggestions are available on the net, Mocha being the prominent one. But in all available examples, actual DB call or external service call happens. My requirement is not to have actual external service call, but just mock the call (Just how, we do using EasyMock in Java). Any help would be highly appreciated. Is it possible to create a mock of DB call or external service call in node js ?
You can use Sinon with Mocha to provide stubbing, or spying of calls. It is important to also write you code to aid mocking. For instance, don't assume the database handler exists in the web component, pass it in through the constructor. That way its easy for testing to replace the database with your own "mocked" version.
As an example - here is the start of one of my bunch of tests where I instanciate a mocked connection pool to my database handler and pass it into the test. I am actually also mocking the web server, and just testing the API.
(function(){
'use strict';
const DBHOST = 'dummy';
const DBNAME = 'dummy';
const expect = require('chai').expect;
const sinon = require('sinon');
const mock = require('../mock/server');
const DB = require('../database');
const Logging = require('../log');
Logging.debug(true); //set to log debug message
const logger = Logging.logger;
const poolPromise = mock.getPool();
const db = new DB(mock.Pool,DBNAME,logger);
const API = require('../api');
const api = new API(mock.server,db,logger);
describe('API tests',function() {
let sandbox, testConnection;
beforeEach(function(){
sandbox = sinon.sandbox.create();
testConnection = new mock.Connection();
return poolPromise.then(pool => {
pool.getConnection().then(cb => {
cb(null,testConnection);
});
});
});
afterEach(function(){
sandbox.restore();
});
describe('/logon',function() {
let params;
describe('Admin', function() {
beforeEach(function() {
params = {name: 'Admin', password: 'abcde'};
})
it('Performs logon if username is "Admin" and password is good', function(done) {
mock.server.emit('/logon', params, (status,user) => {
expect(status).to.be.true;
expect(user).to.be.an('object');
expect(user.uid).to.be.equal(0);
expect(user.name).to.be.equal('Super User');
expect(user.keys).to.be.equal('A');
done();
});
testConnection.next.then(value => {
let request = value.request;
expect(value.name).to.equal('execSql');
expect(request.sqlTextOrProcedure).to.equal('SELECT passwordsalt FROM Admin WHERE AdminID = 0');
request.emit('row', mock.makeRow('passwordsalt',['salt']));
request.callback(null,1);
return testConnection.next;
}).then(value => {
let request = value.request
expect(value.name).to.equal('callProcedure');
expect(request.sqlTextOrProcedure).to.equal('CheckPassword');
request.emit('row',mock.makeRow('a',[1]));
request.callback(null,0);
});
});
Related
This is probably pretty easy but I've been unable to piece it together properly.
I'm trying to use the Sequelize NPM to call a stored procedure that I built and then I want to trigger it with a GET request from from an express api and return the output of the procedure to the api.
Here is what my code looks like for the Sequelize portion....
// Testing stored procedure //
const Retrieve = (testName) => connection.testdata_connection.query("EXEC [SPROC] [INPUTS]")
module.exports = {
tests: Tests(),
retrieve: Retrieve()
};
This part "connection.testdata_connection" is just establishing my connection to the database and I have tested this and I know this part is set.
I would like to be able to hit that with something like...
const query = require('./database/queries'); ///Imports sequelize queries
const app = express();
app.get('/decrypt', function(req,res){
query.retrieve()
})
})
This doesn't work at all.
Now if I do something like this in the queries file...
const Retrieve = async function() {
const decrypt = await connection.testdata_connection.query("EXEC [SPROC] [INPUT]")
console.log(decrypt)
}
module.exports = {
tests: Tests(),
retrieve: Retrieve()
};
This will log to my console with correct data when I start the server. I want it to do that when I hit it with my endpoint.
First, your function should be exported but not executed:
// creating an async function (all i/o operations should be async).
const Retrieve = async(testName) => connection.testdata_connection.query("EXEC [SPROC] [INPUTS]")
module.exports = {
retrieve: Retrieve,
// retrieve: Retrieve() if you call the function with (), the function will be executed and we don't want that yet
};
Now we can call it in the route:
const query = require('./database/queries'); ///Imports sequelize queries
const app = express();
// the route is async because it is executing async code
app.get('/decrypt', async (req,res) => {
// waiting for the answer with await
const response = await query.retrieve();
// Doing something with the response
})
You still need to check for errors, but that is the basics.
I'm experimenting with Strapi and would like to create a controller verified by unit tests.
How do I setup Unit tests within Strapi?
I have written the following test
test('checks entity inside boundary',async ()=> {
ctx={};
var result = await controller.findnearby(ctx);
result = {};
expect(result).anyting();
});
however, inside my Controller I have code that accesses a global strapi object, which causes this error ReferenceError: strapi is not defined
strapi.log.info('findNearby');
strapi.log.info(ctx.request.query.lat);
strapi.log.info(ctx.request.query.long);
What is the best practice with Strapi and testing?
I've managed to achieve testing in Strapi by creating a helper
const Strapi = require("strapi");
// above require creates a global named `strapi` that is an instance of Strapi
let instance; // singleton
async function setupStrapi() {
if (!instance) {
await Strapi().load();
instance = strapi;
instance.app
.use(instance.router.routes()) // this code in copied from app/node_modules/strapi/lib/Strapi.js
.use(instance.router.allowedMethods());
}
return instance;
}
module.exports = { setupStrapi };
You can get all of the controllers from app.controllers now and test them one by one.
My example test (in Jest) for API endpoint would look like
const request = require("supertest");
const { setupStrapi } = require("./helpers/strapi");
// We're setting timeout because sometimes bootstrap can take 5-7 seconds (big apps)
jest.setTimeout(10000);
let app; // this is instance of the the strapi
beforeAll(async () => {
app = await setupStrapi(); // return singleton so it can be called many times
});
it("should respond with 200 on /heartbeat", (done) => {
request(app.server) // app server is and instance of Class: http.Server
.get("/heartbeat")
.expect(200) // expecting response header code to by 200 success
.expect("Hello World!") // expecting reponse text to be "Hello World!"
.end(done); // let jest know that test is finished
});
I've tried to cover this topic on my blog https://medium.com/qunabu-interactive/strapi-jest-testing-with-gitlab-ci-82ffe4c5715a
There were some changes, regarding the version upgrade.
I recommend to switch to stable 3.0.0 and use the following snippet
const Strapi = require("strapi");
const http = require("http");
let instance;
async function setupStrapi() {
if (!instance) {
/** the follwing code in copied from `./node_modules/strapi/lib/Strapi.js` */
await Strapi().load();
instance = strapi; // strapi is global now
await instance.app
.use(instance.router.routes()) // populate KOA routes
.use(instance.router.allowedMethods()); // populate KOA methods
instance.server = http.createServer(instance.app.callback());
}
return instance;
}
module.exports = { setupStrapi };
Here is my example project.
Guide for this is hopefully coming to Strapi documentation sooner then later.
I don't understand how to spy whether a method/function has been fired inside a module.
This is my example:
db.js
module.exports.saveUser = (user) => {
console.log('Saving the user', user);
};
app.js
let db = require('./db');
module.exports.handleSignup = (email, password) => {
db.saveUser({ email, password });
}
app.test.js
const db = require('./db');
jest.genMockFromModule('./app');
const app = require('./app');
describe('App: ', () => {
it('should call "db.saveUser" with a user object', () => {
let dbSpy = jest.spyOn(db, 'saveUser');
const user = { email: 'email#email.com', password: 'abc123' };
app.handleSignup(user.email, user.password);
expect(dbSpy).toBeCalled();
});
});
by running this test I can see from the shell the console.log written in saveUser(), so it means that db.saveUser() is firing and Jest is not mocking up saveUser().
What am I doing wrong?
referring to the manual mock example given here:
https://facebook.github.io/jest/docs/en/manual-mocks.html
jest.genMockFromModule() is actually called in a mock module file. Even so, if that db file you're mocking is one of your own local files, you wouldn't need to call genMockFromModule. I believe that that's only used for the npm modules in your node_modules folder and core modules (e.g. 'fs', 'readline', etc.)
that aside
I've been working with Spock for a while and I've found mocking and observing to be a bit of a trial-and-error process. Sometimes you have to get a bit creative with it as it doesn't quite perform as you might expect.
An easy approach that has worked for me in multiple instances (and, I imagine, will work in your case):
create a mock:
./__mocks__/db.js (I'm assuming db.js is in your project's base dir folder)
module.exports = {
fileSaveObserver: [],
getFileSaveObserver () {
return this.fileSaveObserver;
},
saveUser (obj) {
this.fileSaveObserver.push(obj);
}
}
then in ./__tests__/app.test.js:
jest.mock('./db.js');
const app = require('../app');
describe('App: ', () => {
test('should call "db.saveUser" with a user object', () => {
const user = { email: 'email#email.com', password: 'abc123' };
app.handleSignup(user.email, user.password);
const db = require('./db.js');
const observer = db.getFileSaveObserver()
expect(observer.length).toBe(1)
expect(observer).toEqual(user);
})
if you're having problems with the above, try removing the '.js' file extensions from the jest.mock() statement and the require statement (this has been a gotcha for me on some occasions.
hope that helps!
Suppose I have a the following module, as database.js
const initOptions = {}
const pgp = require('pg-promise')(initOptions)
const config = require('../../config')
const db = pgp({
host: config.database.host,
port: config.database.port,
database: config.database.database,
user: config.database.user,
password: config.database.password
})
module.exports = db
And the following module as create.js
const db = require('./database')
function create (name) {
return new Promise((resolve, reject) => {
db.func('create', name)
.then(data => {
return resolve(data)
})
.catch(err => {
return reject(err)
})
})
}
module.exports = create
I'm trying to run a unit test on create.js that will test that db.func is called with 'create' as first argument and 'name' as the second, but without actually needing to set up a database connection (So tests can run offline).
From what I can gather, this is what libraries like sinon.JS can be used for, so I tried creating a test and stubbed the db object.
const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
chai.use(chaiAsPromised)
const sinon = require('sinon')
const expect = chai.expect
const create = require('./create.js')
describe('Test Module', () => {
it('should test stuff', () => {
const db = require('./database')
const dbStub = sinon.stub(db, 'func').callsFake(Promise.resolve(null))
expect(create('test').then).to.be.a('Function')
})
})
However, it fails with
TypeError: Cannot redefine property: func
Most likely due to my limited exposure to sinon...
How do I go about stubbing (or maybe I need to mock?) the db function so that I can test it an ensure db.func was called?
You can make the properties configurable by disabling locks with the no noLocking option in Initialization Options. This allows sinon to replace the properties:
const initOptions = { noLocking : true };
On a related note:
Your create function is creating an unnecessary promise wrapper, which is a promise anti-pattern. You should just return the result from db.func, which is a promise already:
function create(name) {
return db.func('create', name);
}
Also callsFake takes a function and you are giving it a promise. Use returns instead:
const dbStub = sinon.stub(db, 'func').returns(Promise.resolve(null))
I'm having trouble setting the noLocking option. The docs state it can be set after initialization, however if I set it with db.$config.options.noLocking = true, the same error occurs. However, if I set it in the database.js init options it works fine.
From the author of pg-promise...
It is because at that point the noLocking can only affect tasks and transactions. And since the db level of the protocol is initiated only once, setting noLocking after the library's initialization doesn't effect it.
I have just updated the documentation to clarify it:
This option is dynamic (can be set before or after initialization). However, changing it after the library's initialization will not affect Database objects that have already been created.
Basically to run the tests, I need the database connection to MongoDb to be ready. The database manager has a connect method to get a connection and a get method to get a reference to that connection. The connect method is supposed to be called only once, at startup. This design works fine when running the app because the app starts only if the connect method has been called and returned a connection.
The database manager using mongo native (no mongoskin, no mongoose) (database.js):
var config = require('../config')
var MongoClient = require('mongodb').MongoClient
var connection = null
module.exports.connect = function () {
return MongoClient.connect(config.mongodb.uri + config.mongodb.db)
.then(function (db) {
connection = db
})
}
module.exports.get = function () {
if (!connection) {
throw new Error('Call connect first!')
}
return connection
}
Start the app:
var db = require('./services/database');
db.connect()
.then(function() {
logger.info("mongodb is running");
require('./main')
});
In the application, there are service modules and repository modules. The service modules require repository modules when they have to use the database. I have a BaseRepository module where I define the common queries findOne, findAll, etc. For all the MongoDb collections I have a specific repository that inherits from the BaseRepository. In the constructor, I set the collection by calling the database connection.
It means that when a service module which has a dependency on a repository module, the database connect method has to be called. This is the issue because I don't know how to start all the tests after the call to that connect method. I am open to suggestions if you think it's a design flow.
A test:
var usersService = require('../../services/rest/UsersService');
describe('IT Test', function () {
})
The service:
var usersRepository = require('../dao/UsersRepository');
The repository:
var db = require('../database');
var BaseRepository = require('./BaseRepository');
var util = require('util');
util.inherits(UsersRepository, BaseRepository);
function UsersRepository() {
this.collection = db.get().collection('users'); // <====== The connect method has not been called yet
}
The base repository:
function BaseRepository() {
this.collection = undefined;
}
BaseRepository.prototype.findOne = function (filters) {
return this.collection.findOne(filters)
}
I ended up writing a wrapper for all the tests
'use strict'
var logger = require('../services/logger')()
var db = require('../services/database')
describe('All tests wrapper', function () {
it('should pass', function () {
return db.connect()
.then(function () {
logger.info('MongoDB is running')
require('./agenda/AgendaJobsIT')
require('./dashboard/DashboardsServiceIT')
require('./twitter/TwitterServiceTest')
require('./authentication/TokenServiceTest')
})
})
})
The best way is mocha --delayed switch. mocha doc says
If you need to perform asynchronous operations before any of your suites are run (e.g., for dynamically generating tests), you may delay the root suite. Run mocha with the --delay flag.
For example, use mocha in this way mocha --recursive --exit --delay --ui tdd tests.js and --delayed enable you to trigger running the root suite via calling run() explicitly.
const fn = async x => {
return new Promise(resolve => {
setTimeout(resolve, 1500, 2 * x);
});
};
(async function() {
const z = await fn(3);
suite("won't run until run() executes", () => {})
run();
})();
For more information, please read https://mochajs.org/#delayed-root-suite.