node.js mock sendmailer transporter inside function - javascript

Im working with an node handler in AWS lambda and i need to make another files with integration tests from that function, but i cant mock the transporter with sinon or mockery.
the index.js function:
var nodemailer = require('nodemailer');
exports.handler = (event, context, callback) =>
{
var transporter=createTransporter();
transporter.sendMail(data, function (error, success) {
console.log(error);
response = getResponse(404, error);
}
callback(null, response);
});
}
function createTransporter() {
return nodemailer.createTransport({
service: "SMTP",
auth: {
user: "XXXX#XXX",
pass: "XXXX"
}
});
}
the purpose is to mock the function createTransporter() so that it doesnt send any email when it is called in javascript file test with mocha and expect:
var mockery = require('mockery');
var nodemailerMock = require('nodemailer-mock');
var index = require("../index.js");
describe("The handler function tests", function () {
before(function () {
mockery.enable({
warnOnUnregistered: false
});
mockery.registerMock('nodemailer', nodemailerMock);
});
it('JSON error html ', function () {
var callback = function (name, response) {
expect(JSON.stringify(response.statusCode)).to.be('404');
};
var context = {};
index.handler(event, context, callback);
});
});

I wrote nodemailer-mock :)
The problem you're having is that you are calling var index = require("../index.js"); before you are mocking nodemailer via mockery, so it is already in the module cache. I included // Make sure anything that uses nodemailer is loaded here, after it is mocked... in the examples in the README, but should probably make it more clear.
Move the require("../index.js") after nodemailer is mocked and it will be work as expected.
var mockery = require('mockery');
var nodemailerMock = require('nodemailer-mock');
// don't require here since you will get the real nodemailer and cache it
var index;
describe("The handler function tests", function () {
before(function () {
mockery.enable({
warnOnUnregistered: false
});
mockery.registerMock('nodemailer', nodemailerMock);
// do the require() here after nodemailer is mocked
index = require("../index.js");
});
// your tests here should now use nodemailer-mock
it('JSON error html ', function () {
var callback = function (name, response) {
expect(JSON.stringify(response.statusCode)).to.be('404');
};
var context = {};
index.handler(event, context, callback);
});
});
Another option is to use the { useCleanCache: true } option with calls to mockery.resetCache();, though I have had mixed results. See Controlling the Module Cache in the mockery documentation.

I'm not 100% sure why this would fail, but I suggest one of two things:
Try doing var createTransporter = function()... there's a slight difference here that might be your issue
exporting createTransporter so you can assign a new value to it, either a mock or not. This isn't very "keep implementation details private", it does work
Have your module return a class, or object anyway, where you can set some "use this transporter method" value. (ie dependency injection)

You can use the following option from Jest:
jest.mock('nodemailer').setMock(/* function mock for module */)
Remember to use this at the top of the file, before import or require statements.
Here is the official Jest documentation: https://jestjs.io/docs/manual-mocks#mocking-node-modules.

Related

What is the best way to write unit test in node.js?

I have a module where I load a mustache template file. I would like to write a unit test for this. I am trying to use mocha, chai and rewire.
Here is my module.js:
var winston = require('winston');
var fs = require('fs');
var config = require('./config.js');
exports.logger = new winston.Logger({
transports: [
new winston.transports.File(config.logger_config.file_transport),
new winston.transports.Console(config.logger_config.console_transport)
],
exitOnError: false
});
exports.readTemplateFile = function(templateFile, callback) {
fs.readFile(config.base_directory + templateFile + '.tpl.xml', 'utf8', function (err, data) {
if (err) {
logger.error('Could not read template ' + templateFile + ': ' + err);
}
callback(data);
});
};
In the callback function I use mustache to do something with the template.
What is the best way to test this?
Maybe I will have to rewire the fs.readFile? As the file won't be there when the test will be executed. The Winston logger is also an interesting part I guess, not sure if it will be initialized if I import this within a mocha test. My first test shows the logger is undefined.
One of the most important unit testing principle is testing very small piece of code. To achieve that you should definitely mock or stub calls to functions that not belong to testing code (readFile and logger.error in this case). For provided code you can make three test cases:
calling readFile with proper arguments
calling error if err is present
calling callback function with proper argument
Your callback function should be tested outside this code, for e.g. by providing fake data as parameter:
define('Some test', () => {
it('should return true', () => {
expect(callbackFunction('fakeData').to.be.ok);
});
});

Mocking console.log()/Any Other Function in MOCHA testing framework

I am writing test cases for NODE JS API. But wherever console.log() is there in routes or services of NODE JS File, it gets printed to CLI. Is there a way to mock these so that these won't get printed in CLI.
I have explored couple of libraries like Sinon, Stub for mocking. But couldn't grasp the working of those libraries.
You can override function entirely: console.log = function () {}.
You should not try to mock console.log itself, a better approach is for your node modules to take a logging object. This allows you to provide an alternative (ie. a mock) during testing. For example:
<my_logger.js>
module.exports = {
err: function(message) {
console.log(message);
}
}
<my_module.js>
var DefaultLogger = require('my_logger.js');
module.exports = function(logger) {
this.log = logger || DefaultLogger;
// Other setup goes here
};
module.exports.prototype.myMethod = function() {
this.log.err('Error message.');
};
<my_module_test.js>
var MyModule = require('my_module.js');
describe('Test Example', function() {
var log_mock = { err: function(msg) {} };
it('Should not output anything.', function() {
var obj = new MyModule(log_mock);
obj.myMethod();
});
});
The code here I've simplified, as the actual test isn't the reason for the example. Merely the insertion of alternative logging.
If you have a large codebase with lots of console.log calls, it is better to simply update the code as you add tests for each method. Making your logging pluggable in this way makes your code easier and more receptive to testing. Also, there are many logging frameworks available for node. console.log is fine during development when you just want to dump out something to see what's going on. But, if possible, try to avoid using it as your logging solution.
I could not find a solution which only hides the console.log calls in the module to be tested, and mocks none of the calls of the testing framework (mocha/chai in my case).
I came up with using a copy of console in the app code:
/* console.js */
module.exports = console;
/* app.js */
const console = require('./console');
console.log("I'm hidden in the tests");
/* app.spec.js */
const mockery = require('mockery');
var app;
before(() => {
// Mock console
var consoleMock = {
log: () => {}
}
mockery.registerMock('./console', consoleMock);
// Require test module after mocking
app = require('./app');
});
after(() => {
mockery.deregisterAll();
mockery.disable();
});
it('works', () => {});
You could do something along the lines of adding these before/after blocks to your tests, but the issue is that mocha actually uses console.log to print the pretty messages about the results of the test, so you would lose those
describe('Test Name', function() {
var originalLog;
beforeEach(function() {
originalLog = console.log;
console.log = function () {};
});
// test code here
afterEach(function() {
console.log = originalLog;
})
})
The problem is that your output would just look like
Test Name
X passing (Yms)
Without any intermediate text

Why doesn't sinon recognize my stub?

Let's say I have a module that exports like this:
module.exports = mymodule;
Then in my test file, I require the module and stub it.
var mymodule = require('./mymodule');
describe('Job gets sports data from API', function(){
context('When there is a GET request', function(){
it('will call callback after getting response', sinon.test(function(done){
var getRequest = sinon.stub(mymodule, 'getSports');
getRequest.yields();
var callback = sinon.spy();
mymodule.getSports(callback);
sinon.assert.calledOnce(callback);
done();
}));
});
});
That works and the test passes! But everything breaks down if I need to export more than one object. See below:
module.exports = {
api: getSports,
other: other
};
Then I try to adjust my test code:
var mymodule = require('./mymodule');
describe('Job gets sports data from API', function(){
context('When there is a GET request', function(){
it('will call callback after getting response', sinon.test(function(done){
var getRequest = sinon.stub(mymodule.api, 'getSports');
getRequest.yields();
var callback = sinon.spy();
mymodule.api.getSports(callback);
sinon.assert.calledOnce(callback);
done();
}));
});
});
In this case, my test craps out. How can I change my stub code to work? Thanks!
Based on this
module.exports = {
api: getSports,
other: other
};
it looks like mymodule.api doesn't itself have a getSports method. Rather, mymodyle.api is a reference to a getSports function insider your module.
Instead of stubbing getSports you would need to stub api:
var getRequest = sinon.stub(mymodule, 'api');
However, given how you're trying to stub getSports, you might instead want to update how you are exporting the function instead of how you are stubbing it.

How to mock event handler method using sinon.js?

I am newbie to Node.js and I am writing DAO layer for HBase which will wrap thrift and provide clear interface to other layers. I am trying to write unit tests for it using sinon.js and mocha but not sure how to ensure mock one event of Thrift connection class and its event handler.
My DAO code is as follows:
var thrift = require('thrift');
var libDirRelativePath = "../../../lib";
var hbaseThriftDirPath = libDirRelativePath + "/hbase-gen-nodejs";
var hbase = require(hbaseThriftDirPath + '/THBaseService');
var hbaseTypes = require(hbaseThriftDirPath + '/hbase_types');
var thritfPrimaryServerAddress = 'nn2';
var thritfBackupServerAddress = 'backup-nn2';
var thriftServerPort = 9090;
exports.putRows = function(tableName, putObjectArray, callback) {
var primaryClusterConnection = thrift.createConnection(thritfPrimaryServerAddress, thriftServerPort, {
transport: thrift.TBufferedTransport,
protocol : thrift.TBinaryProtocol
});
console.log('DEBUG : connection object created.');
var client = thrift.createClient(hbase, primaryClusterConnection);
console.log('DEBUG : client object created.');
primaryClusterConnection.on('connect', onConnectOfPutRows);
primaryClusterConnection.on('connect', function() {
console.log('Connected to HBase thrift server at ' + thritfPrimaryServerAddress + ":" + thriftServerPort);
client.putMultiple(tableName, putObjectArray, callback);
connection.close();
});
primaryClusterConnection.on('error', function() {
console.log('Error occurred in HBase thirft server connection.');
});
}
For above code I Just want to create stubs primaryClusterConnection and client objects which I have managed but problem is that stub of primaryClusterConnection doesn't have any idea about connect event and its handler so console.log('Connected to HBase thrift server at '... line never gets executed. I want to test that part of the code as well. Can anyone please help me in writing proper stubs/mocks for this problem?
My test code is as follows:
var hbaseDao = require('../../../src/dao/hbase/HBaseDao.js');
var libDirRelativePath = "../../../lib";
var hbaseThriftDirPath = libDirRelativePath + "/hbase-gen-nodejs";
var hbase = require(hbaseThriftDirPath + '/THBaseService');
var chai = require('chai');
var should = chai.should();
var expect = chai.expect;
var sinon = require('sinon');
describe("HBaseDao", function() {
describe(".putRows()", function() {
it("Should execute callback after inserting objects in HBase.", function(done) {
var commonStub = sinon.stub();
var connection = {
close : function() {
console.log('connection closed.');
}
};
commonStub.withArgs('nn2', 9090).returns(connection);
var client = {};
commonStub.withArgs(hbase, connection).returns(client);
var tableName = 'DUMMY_READINGS_TABLE';
var callBackMethod = function() {
console.log('dummy callback function.');
};
commonStub.withArgs(tableName, [], callBackMethod).returns(0);
hbaseDao.putRows(tableName, [], callBackMethod);
expect(hbaseDaoSpy.callCount).to.equal(1);
done();
});
Let's start by simplifying the problem a bit.
it.only("Should execute callback after inserting objects in HBase.", function(done) {
var events = require('events');
var hbaseDao = new events.EventEmitter();
hbaseDao.putRows = function() {
console.log('putting rows');
this.emit('notify');
};
hbaseDao.on('notify', function(){
console.log('notify event fired');
done(); //here's where you call the callback to assert that the event has fired
});
sinon.spy(hbaseDao, 'putRows');
var commonStub = sinon.stub();
var tableName = 'DUMMY_READINGS_TABLE';
var client = {};
var connection = {
close : function() {
console.log('connection closed.');
}
};
var callBackMethod = function() {
console.log('dummy callback function.');
};
commonStub.withArgs('nn2', 9090).returns(connection);
commonStub.withArgs({}, connection).returns(client);
commonStub.withArgs(tableName, [], callBackMethod).returns(0);
hbaseDao.putRows(tableName, [], callBackMethod);
//assertions
assert(hbaseDao.putRows.calledOnce);
});
The above test will just work, because it creates a new "hbaseDao" from a simple event emitter and has the method and the notify event ready to go.
Because we're doing an async test, we need to have the done callback in the spec. Notice that this will only fire "done" when the event has occurred. Hence, the test will not pass unless the event fires. Also notice that we're spying specifically on the the hbaseDao 'putRows' and we're asserting that the its called once, another way to ensure that the test is working. Now consider this example and apply it to your original question.
I think you almost got it, but you need to put your done callback in the callback stub as so:
var callBackMethod = function() {
console.log('dummy callback function.');
done();
};
That way, when your primaryClusterConnection.on('connect') event is fired, the supplied callback will execute the done and complete the test.
That being said, you should leave your primaryClusterConnection intact and let the implementation details of hbaseDao not be considered in your test.
You mentioned that:
primaryClusterConnection doesn't have any idea about connect
But that can't be right, because you're creating a new connection in the test and there's nothing in your implementation that tells me you have changed the event handler for the connection.
So I think in the end, you're missing the point of the test, which is simply should execute callback... and you're stubbing out stuff that you don't even need to.
Try something like this:
//use it.only to make sure there's no other tests running
it.only("Should execute callback after inserting objects in HBase.", function(done) {
//get the class
var hbaseDao = require('../../../src/dao/hbase/HBaseDao.js');
//spy on the method
sinon.spy(hbaseDao, 'putRows');
//create a table name
var tableName = 'DUMMY_READINGS_TABLE';
//create callback method with done.
var callBackMethod = function() {
console.log('dummy callback function.');
done();
};
//run the function under test
hbaseDao.putRows(tableName, [], callBackMethod);
//assert called once
assert(hbaseDao.putRows.calledOnce);
});

JS - passing a connection(object) from one function to another

I am new to JS and I have a simple question. I am writing a node_redis code to connect to the db.
I have created a db module in which there is an init function to start the connection.
the module also has another function which queries the db. for the query, i will need the connection (dbConnection) object from the first function and then use it in the 2nd function. how do i do this? I can get it done by using global variables but most places tell me its a bad idea.
Sorry if the question is stupid, I am learning how to code. results in google tell me that it can be done by passing it as an object property. But i don't know if it is the proper way to do things in my context, or even how to do it.
var redis = require('redis');
module.exports = redisDb = {
// Initialize the module. Invokes callback when ready (or on error)
init: function(config, callback) {
// Open the database connection
var dbConnection = redis.createClient(config.db.port, config.db.host, {no_ready_check: true});
dbConnection.auth(config.db.authKey, function() {
console.log("Connected!");
console.log(dbConnection.keys('*'));
});
dbConnection.on('connect' , log('connect'));
dbConnection.on('ready' , log('ready'));
dbConnection.on('reconnecting', log('reconnecting'));
dbConnection.on('error' , log('error'));
dbConnection.on('idle' , log('idle...'));
dbConnection.on('end' , log('end'));
function log(type) {
return function() {
console.log(type, arguments);
}
}
callback("callback - Connected");
},
getValue: function(key, callback) {
dbConnection.hgetall("hosts", function (err, obj) {
console.dir(obj);
});
}
};
EDIT:
tried another way. still failing.
module.exports = redisDb = (function() {
var config = require('../config');
var redis = require('redis');
return {
connection: function(config) {
var dbConnection = redis.createClient(config.db.port, config.db.host, {no_ready_check: true});
dbConnection.auth(config.db.authKey, function() {
console.log("Authenticated!");
});
return dbConnection
},
getValue: function(connection, callback) {
connection.hgetall("hosts", function (err, obj) {
console.dir(obj);
});
}
}
})();
Now one way is as you said to make dbConnection an object Property.
The other way is to call init() in getValue() which results in establishing different connection every time you want some value.
Because a DB Connection is a valuable resource, I'd say it's better to use the second variant.
Of course then you'll need to return dbConnection from init().
PS: Global variables were made initially for such things and then people saw that fewer they are the better. That's why using global vars is considered a bad style.
I made the code work by simply declaring the connection in the parent function.
module.exports = _redisDb = (function() {
var redis = require('redis');
var config = require('../config');
var dbConnection = redis.createClient(config.db.port, config.db.host, {no_ready_check: true});
dbConnection.auth(config.db.authKey, function() {
console.log("Authenticated!");
});
var getValue = function() {
dbConnection.hgetall("hosts", function (err, obj) {
console.dir(obj);
});
}
return {
getValue: getValue
}
})();
While the above work,s I'd still like for someone to tell me how can I pass variables/objects between functions off the same module.

Categories