Mocha calling callback after done() - javascript

I am aiming for ease of use in my unit tests, I have created a master_test.js file that I will include below. In it, I simply require other test.js files as a function and run them. An issue I had is that Mocha does not detect any unit tests to be run unless I encase my startup in a describe() block, so I am forced to have a master describe encase my application to resolve that. My problem now is that in order to run the other tests, I have to call a callback() after a done(). Is there any easy way to fix my problem?
function app_setup() {
options.database = 'testing';
it('app launched successfully', function(done) {
require('../app.js').initialize(0, options, function(err, app) {
remove_all_entities(function() {
app.set('port', port);
var server = app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + server.address().port);
//ISSUE LOCATION, NEED TO CALL run_tests() callback after done()//
done();
run_tests();
});
});
});
});
}
function run_tests() {
var database = require('../database.js');
var entity_controller_test = require('./controllers/entity_controller_test.js').entity_controller_test;
var login_test = require('./controllers/login_test.js').login_test;
var token_access_test = require('./controllers/token_access_test.js').token_access_test;
var token_auth_test = require('./controllers/auth_token_test.js').token_auth_test;
var business_rules_insert = require('./business_rules/basic_database_rules.js').business_rules_insert_test;
var logout_test = require('./controllers/logout_test.js').logout_test;
var schema_override = require('./business_rules/schema_overrides').schema_overrides;
var aggregation_test = require('./entity_specific/aggregation').aggregation_test;
var tests = [login_test, aggregation_test, logout_test];
async.series(tests, function() {
test_teardown(done);
});
}
function test_teardown(done) {
remove_all_entities(done);
};

Related

No output from jasmine-node on FilesizeWatcherSpec - Newbie Alert

I'm new to Node.js and jasmine, and my JavaScript experience is old and rusty, so I'm a newbie there too. I finished Manuel Kiessling's book, The Node Beginner Book, and I am working my way through his second book, The Node Craftsman Book. I'm stuck on the FilesizeWatcher tutorial. I've been able to run earlier tests but this one is not working. There is a similar question on SO: No output from jasmine-node but the answer isn't working for me.
I'll post my code here and hopefully somebody can tell me what I'm doing wrong.
FilesizeWatcherSpec.js:
'use strict';
var FilesizeWatcher = require('./FilesizeWatcher');
var exec = require('child_process').exec;
describe('FilesizeWatcher', function() {
var watcher;
afterEach(function() {
watcher.stop();
});
it('should fire a "grew" event when the file grew in size', function(done) {
var path = './var/tmp/filesizewatcher.test';
exec('rm -f ' + path + ' ; touch ' + path, function() {
watcher = new FilesizeWatcher(path);
watcher.on('grew', function(gain) {
expect(gain).toBe(5);
done();
});
exec('echo "test" > ' + path, function(){});
});
});
it('should fire a "shrank" event when the file shrank in size', function(done) {
var path = './var/tmp/filesizewatcher.test';
exec('rm -f ' + path + ' ; echo "test" > ' + path, function() {
watcher = new FilesizeWather(path);
watcher.on('shrank', function(loss) {
expect(loss).toBe(3);
done();
});
exec('echo "a" > ' + path, function(){});
});
});
it('should fire an "error" if path does not start', function(done) {
var path = 'var/tmp/filesizewatcher.test';
watcher = new FilesizeWather(path);
watcher.on('error', function(err) {
expect(err).toBe('Path does not start with a slash');
done();
});
});
});
FilesizeWatcher.js:
'use strict';
var fs = require('fs');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var FilesizeWatcher = function (path) {
var self = this;
if (/^\//.test(path) === false) {
process.nextTick(function() {
self.emit('error', 'Path does not start with a slash');
});
return;
}
fs.stat(path, function (err, stats) {
console.log('stats= ' + stats);
self.lastfilesize = stats.size;
});
self.interval = setInterval(
function () {
console.log('We are in function()');
fs.stat(path, function (err, stats) {
if (stats.size > self.lastfilesize) {
self.emit('grew', stats.size - self.lastfilesize);
self.lastfilesize = stats.size;
}
if (stats.size < self.lastfilesize) {
self.emit('shrank', self.lastfilesize - stats.size);
self.lastfilesize = stats.size;
}
}, 1000);
});
};
util.inherits(FilesizeWatcher, EventEmitter);
FilesizeWatcher.prototype.stop = function () {
clearInterval(this.interval);
};
module.exports = FilesizeWatcher;
Console output:
C:\Users\pdl\Projects\NodeCraftsman>jasmine-node ./FilesizeWatcherSpec.js
C:\Users\pdl\Projects\NodeCraftsman>
Other tests run fine:
C:\Users\pdl\Projects\NodeCraftsmanTestDrivenDevelopment>jasmine-node spec\greetSpec.js
..
Finished in 0.006 seconds
2 tests, 2 assertions, 0 failures, 0 skipped
C:\Users\pdl\Projects\NodeCraftsmanTestDrivenDevelopment>
I added --captureExceptions to see if I could get any information and I got the TypeError: self.callbacks.error is not a function.
My first problem was as Eppilo suggested below, that I needed to use process.nextTick on self.callbacks'error'. Mixing async code with sync code causes the error event to be fired before the error handler is registered. So I made the changes and am now using the EventEmitter but I'm still getting the following errors:
If I include the "." in the path: var path = './var/tmp/filesizewatcher.test'; then the file gets written. Otherwise, it does not.
If the file does NOT get written, stats= undefined and I receive this error:
TypeError: Cannot read property 'size' of undefined
at C:\Users\pdl\Projects\NodeCraftsman\FilesizeWatcher.js:19:34
at FSReqWrap.oncomplete (fs.js:82:15)
If the file DOES get written, then I receive this error:
Error: Uncaught, unspecified "error" event. (Path does not start with a slash)
at emit (events.js:144:17)
at C:\Users\pdl\Projects\NodeCraftsman\FilesizeWatcher.js:12:18
at nextTickCallbackWith0Args (node.js:419:9)
at process._tickCallback (node.js:348:13)
Of course, it's not supposed to start with a slash. That is the test. But when I remove the --captureExceptions from the command, I still get no output.
First of all try and run Jasmine on verbose mode and capture exceptions:
jasmine-node ./FilesizeWatcherSpec.js --verbose --captureExceptions
Link: https://github.com/mhevery/jasmine-node/wiki/Command-Line-Usage
Also try to make the error checking asynchronous:
if (/^\//.test(path) === false) {
process.nextTick(function() {
self.callbacks['error']('Path does not start with a slash');
});
return;
}
Newbie as well, with not enough reputation to comment.
I got the same no output on my Mac too, and was able to get the test to work with this.
There is an error in FilesizeWatcher.js.
Currently:
self.interval = setInterval(
function (){
...
fs.stat(path, function (err, stats) {
...
}, 1000);
});
It should instead be:
self.interval = setInterval(
function (){
...
fs.stat(path, function (err, stats) {
...
});
},1000);
Just sharing my findings, cheers.

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);
});

Share methods across processes in node.js

So I want to be able to share methods across different node.js processes created using the cluster module.
If I run the code below I can share the method server.handleRequest across the child processes, however If I uncomment //server.test(); in the second file and try to use the method test in the original process, node crashes.
"use strict";
var os = require('os');
var http = require('http');
var cluster = require('cluster');
function testMethod() {
console.log('Test');
}
function handleRequest(req, res) {
res.writeHead(200);
res.end("This answer comes from the process " + process.pid);
}
var createServer = function createServer(opts) {
var server = {};
server.test = testMethod;
server.handleRequest = handleRequest;
if (cluster.isMaster) {
var cpuCount = require('os').cpus().length;
for (var i = 0; i < cpuCount; i += 1) {
cluster.fork();
}
return server;
} else {
// Create HTTP server.
http.Server(function(req, res) {
server.handleRequest(req, res);
}).listen(8080);
}
}
module.exports = {
createServer: createServer,
};
The second file that includes the above file.
"use strict";
var router = require('./test.js');
var server = router.createServer();
//server.test();
But If I don't use the cluster module I can use the test method outside the factory function with out crashing. So how do I share methods created in factory functions across all node processes, while using the cluster module? And why do the child processes execute test when only the original process calls server.test()?
var http = require('http');
function testMethod() {
console.log('Test');
}
function handleRequest(req, res) {
res.writeHead(200);
res.end("This answer comes from the process " + process.pid);
}
var createServer = function createServer(opts) {
var server = {};
server.test = testMethod;
server.handleRequest = handleRequest;
http.Server(function(req, res) {
server.handleRequest(req, res);
}).listen(8080);
return server;
}
module.exports = {
createServer: createServer,
};

Rookie error while writing test with Chai, Mocha, Express, johnny-five and node

Hi there I'm trying to learn a bit of test driven development using express, mocha, chai and johnny-five. So I wrote this little application that can turn an LED on and off. The application works but my test fails. Can somebody tell me what I am doing wrong in my test?
Thank you
The output of npm test is
> blink#1.0.0 test /Users/me/Documents/johnny-five/blink
> mocha --reporter spec
1435257445439 Looking for connected device
j5
.on()
1) Should turn a led on
.off()
✓ Should turn a led off
1 passing (13ms)
1 failing
1) j5 .on() Should turn a led on:
AssertionError: expected undefined to equal 1
at Context.<anonymous> (test/j5.js:9:14)
npm ERR! Test failed. See above for more details.
This is test/j5.js
require('mocha');
var assert = require('chai').assert;
var j5 = require("../j5");
describe('j5', function () {
describe('.on()', function () {
it('Should turn a led on',function(){
var result = j5.on();
assert.equal(result, 1);
});
});
describe('.off()', function () {
it('Should turn a led off', function () {
// var res = j5.on();
// expect(res).to.equal(0);
});
});
});
This is server.js
var express = require("express");
var app = express();
var j5 = require("./j5");
var port = 3000;
app.get('/', function(req, res){
res.send('hello j5');
});
app.get("/on", function(req, res) {
j5.on();
res.send("on");
});
app.get("/off", function(req, res) {
j5.off();
res.send("off");
});
console.log("listening on port http://localhost:" + port);
app.listen(3000);
This is j5.js
var exports = module.exports = {};
var five = require("johnny-five");
var board = new five.Board();
var board_ready = false;
var led = null;
board.on("ready", function() {
board_ready = true;
led = new five.Led(13);
});
exports.on = function() {
if (led !== null && board_ready === true) {
led.on();
return 1;
}
};
exports.off = function() {
if (led !== null && board_ready === true) {
led.off();
return 0;
}
};
EDIT: The path to my j5.js in test/j5.js was wrong. but now I have a new error. AssertionError: expected undefined to equal 1 at Context. (test/j5.js:9:14).
After some playing around I found my error.
johnny-five needs some time to connect to the board via the serial. As soon as the build in REPL is available I can use the functions on() and off(). So I made my test wait for 5 seconds before making the call of j5.on(). The standard max timeout for the done() function is 2000ms. To make this longer I used this.timeout(10000);
This is my new test/j5.js
require('mocha');
var assert = require('chai').assert;
var j5 = require("../j5");
var result = null;
describe('j5', function() {
describe('.on()', function() {
it('Should turn a led on', function(done) {
this.timeout(10000);
setTimeout(function() {
result = j5.on();
assert.equal(result, 1);
done();
}, 5000);
});
});
});
Result of npm test:
> blink#1.0.0 test /Users/icke/Documents/johnny-five/blink
> mocha --reporter spec
1435305595110 Device(s) /dev/cu.usbmodem1421
1435305595124 Connected /dev/cu.usbmodem1421
j5
.on()
1435305598694 Repl Initialized
✓ Should turn a led on (5003ms)
.off()
✓ Should turn a led off
2 passing (5s)

Node.JS run Sandbox in REST service

I am using Node Restify Module to create a REST service that accepts POST. Within the service I am trying to create a Sandboxed process using Node Sandbox module because we will be running dynamically inserted Javascript and if something goes wrong, I dont want it to affect the main Node instance.
When I try to create the Sandbox, something goes wrong and causes the REST service to come back empty.
Here is my code
var restify = require('restify');
var Sandbox = require("sandbox");
var logic;
function createSandbox(body) {
var s = new Sandbox();
s.run("1 + 1", function(output) {
logic = body.names + " has " + output.result;
});
}
function respond(req, res, next) {
createSandbox(req.body);
res.send(logic);
}
var server = restify.createServer();
server.use(restify.bodyParser({
mapParams: false
}));
server.post('/hello/:name', respond);
server.head('/hello/:name', respond);
server.listen(8080, function() {
console.log('%s listening at %s', server.name, server.url);
});
In my http request I have {"names":"rob"} in the body
I am expecting the following response
rob has 2
------------UPDATE-------------------
This works
var restify = require('restify');
var Sandbox = require("sandbox");
var logic;
function respond(req, res, next) {
var s = new Sandbox();
s.run("1 + 1", function(output) {
logic = req.body.names + " has " + output.result;
res.send(logic);
});
}
var server = restify.createServer();
server.use(restify.bodyParser({
mapParams: false
}));
server.post('/run/:id', respond);
server.head('/run/:id', respond);
server.listen(8080, function() {
console.log('%s listening at %s', server.name, server.url);
});
Sandbox.run() is asyncronous. It just sets the sandbox up to run at a later time and returns immediately before the code it sandboxes is actually run, so you're reading logic before it's set.
A quick demo;
var Sandbox = require("sandbox");
function createSandbox() {
var s = new Sandbox();
s.run("1 + 1", function(output) {
console.log("inside");
});
}
createSandbox();
console.log("outside");
> outside
> inside

Categories