Testing: Mocha and node differ re fs.exists - node.js

First, while I'm an experienced programmer, I'm relatively new to node, and very new to mocha, so my issue may be ME! In which case, I'll be happy to find that out too!
Some code I'm writing makes use of an npm module which contains a call to fs.exists. I get different results when I run a test with fs.exists when run via node vs when run vs mocha; in my opinion, I should be receiving the same answer; the file does exists, so the result should be true in both cases, but instead, it's true when invoked via node, and false when invoked via mocha. Well, it's not functioning correctly.
I'm aware that fs.exists is deprecated, but that's in the npm module I'm using, so unless I modify the module, that's what I'm using.
The difference is associated with how I invoke mocha.
Per some site recommendations, I edited my package.json file to include, in the 'scripts' section, the following statement: "test": "mocha test", and I have placed my tests in this 'test' directory. This is invoked via npm test.
After experiencing problems, I also installed did npm install -g mocha.
My test file is testVersion.js
The result is that I get different answers when I invoke one of the following three ways when invoking fs.exists via node, npm test, and mocha testVersion.js; I'm expecting 'true':
- node testVersion.js returns true - it finds the file.
- mocha testVersion.js returns true - it finds the file.
- npm test returns false - it does not find the file.
I'm suspecting that maybe different versions of mocha or node are being invoked on me, but don't have enough experience to be able to determine this on my own.
So my question is: why am I getting different results?
Here's my code for testVersion.js
var expect = require('chai').expect;
var assert = expect.assert;
var fs = require("fs");
var isInTest = typeof global.it === 'function'; //exe via mocha or node
console.log('isInTest mode: ', isInTest);
if(!isInTest) {
console.log('NOT TEST MODE: invoking fs.exists');
fs.exists("testVersion.js", function(info) {
console.log('NOT TEST MODE: fs.exists callback RAN.');
console.log('NOT TEST MODE: fs.exists: should be true and is: ', info);
})
}
if(isInTest) {
describe("Test Mocha and fs.exists issue", function() {
it("Test that file self exists.", function() {
console.log('TEST MODE: invoking fs.exists');
expect(fs.exists("testVersion.js", function(result) {
console.log('TEST MODE: fs.exists callback RAN.');
console.log('TEST MODE: fs.exists: should be true, and is: ', result);
return true;
})).to.equal(true);
});
});
};

After a lot of testing work, I believe my problem was because of async calls associated with fs.exists. At any rate, the following is working for me, and I wanted to document it in case it helps anyone else.
var assert = require('assert');
var fs = require('fs');
fs.existsSync("bbq.js", function(result) {
console.log('False: fs.exists(bbq.js) says: ', result);
})
fs.existsSync("test2.js", function(result) {
console.log('True: fs.exists(test2.js) says: ', result);
})
describe('Testing Synch fs.existsSync() ===', function() {
describe('False: fs.exists(bbq.js)', function() {
it("This assertion should pass, as we are asserting false on file that doesn't exist.", function() {
assert.equal(false, fs.existsSync(__dirname + "/bbq.js", function(result) { return result;}))
});
});
describe('Testing Synch fs.existsSync(test2.js method A)', function() {
it("This assertions should pass as we are asserting true on file that does exist", function() {
assert.equal(true, fs.existsSync(__dirname + "/test2.js", function(result) {
return result;
}));
});
});
describe('Testing Synch fs.existsSync(test2.js method B)', function() {
it("This assertions should pass as we are are asserting true on file that does exist using callback", function() {
fs.existsSync(__dirname + "/test2.js", function(result) {
assert.equal(true, result);
});
});
});
});
////////////////////
describe('Asynch test of fs.exists() === some results are UNDEFINED because of async', function() {
describe('False: fs.exists(bbq.js)', function() {
it("This assertion should pass as we are expecting undefined result due to async.", function() {
assert.equal(undefined, fs.exists(__dirname + "/bbq.js", function(result) { return result;}))
});
});
describe('True: fs.exists(test2.js method A)', function() {
it("This assertion should pass as we are expecting undefined result due to async.", function() {
assert.equal(undefined, fs.exists(__dirname + "/test2.js", function(result) {
return result;
}));
});
});
describe('True: fs.exists(test2.js method B)', function() {
it("This equal assertion passes, because of use of callback waits for response.", function() {
fs.exists(__dirname + "/test2.js", function(result) {
assert.equal(true, result);
});
});
});
});

Related

How to test promise rejection with Mocha and Chai in Node?

I am trying to test my services and dao using Mocha and Chai. But, in Istanbul coverage, I am getting the 'reject' lines as red. Here is the code for a sample testing method.
describe('findAllCategories()', function() {
it('should return all categories', function() {
var stub = sinon.stub(categoryDao, 'findAllCategories');
stub.callsFake(() => {
return Promise.resolve(cat);
});
categoryService.findAllCategories().then(response => {
assert.length(response, 1);
}).catch(isError)
.then((err) => {
console.log(err);
assert.isDefined(err);
});
})
});
Now, when I'm logging the error, it is showing "TypeError: assert.length is not a function".
Any way out?
The assert-library does not have a function length, but instead you can use lengthOf() (see https://www.chaijs.com/api/assert/ for more information):
assert.lengthOf(response, 1);

How can I stub a Promise such that my test can be run synchronously?

I am trying to unit test a module by stubbing one of its dependencies, in this case the UserManager
A simplified version of the module is as follows:
// CodeHandler
module.exports = function(UserManager) {
return {
oAuthCallback: function(req, res) {
var incomingCode = req.query.code;
var clientKey = req.query.key;
UserManager.saveCode(clientKey, incomingCode)
.then(function(){
res.redirect('https://test.tes');
}).catch(function(err){
res.redirect('back');
}
);
}
};
};
I'm stubbing the UserManager's saveCode function which returns a Promise such that it returns a resolved Promise, but when I assert that res.redirect has been called, alas at the time of the assertion res.redirect has not yet been called.
A simplified version of the unit test is:
// test
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var res = {
redirect: function() {}
};
var expectedUrl = 'https://test.tes';
var ch;
beforeEach(function() {
sinon.stub(UserManager, 'saveCode').returns(
new RSVP.Promise(function(resolve, reject){
resolve();
})
);
sinon.stub(res, 'redirect');
ch = CodeHandler(UserManager);
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
});
How can I properly stub the promise such that the method under test behaves synchronously?
I've worked out a solution using sinon-stub-promise.
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var ch;
var promise;
var res = {
redirect: function() {}
};
beforeEach(function() {
promise = sinon.stub(UserManager, 'saveCode').returnsPromise();
ch = CodeHandler(UserManager);
sinon.stub(res, 'redirect');
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
describe('can save code', function() {
var expectedUrl = 'https://test.tes';
beforeEach(function() {
promise.resolves();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
});
});
describe('can not save code', function() {
var expectedUrl = 'back';
beforeEach(function() {
promise.rejects();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
})
});
This works perfectly.
Well, the easiest thing would be not to stub it to run synchronously at all since that might change execution order and use Mocha's built in promises support (or jasmine-as-promised if using jasmine).
The reason is there can be cases like:
somePromise.then(function(){
doB();
});
doA();
If you cause promises to resolve synchronously the execution order - and thus output of the program changes, making the test worthless.
On the contrary, you can use the test syntax:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () => {
return methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
});
});
You can use the even lighter arrow syntax for single lines which makes the test even less verbose:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () =>
methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
);
});
In RSVP, you can't set the scheduler as far as I know so it's quite impossible to test things synchronously anyway, other libraries like bluebird let you do it at your own risk, but even in libraries that let you do it it's probably not the best idea.

Testing synchronous code

I have a section of code that is using node-sync like so:
function funcA() {
return new Promise(function(resolve, reject) {
Sync(function () {
return funcB.sync();
}, function (err, result) {
if(err) {
reject(err);
} else {
resolve(result);
}
});
}
This code is tested using mocha+chai:
it("should return array", function() {
return funcA().then(function(result) {
expect(result).to.be.an.instanceof(Array);
});
});
It worked just fine couple of months ago, but now this test always times out:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
What I've tried so far:
using done() instead of returning a promise
replacing node-sync with synchronize.js
increasing timeout
What I found out, is that expect(... part of this test is, in fact, being called, but only after mocha kills the test. No matter what timeout interval is currently set, expect(.. is being called always ~20 milliseconds after I get Error: timeout message.
I fixed the issue by adding setInterval(function(){}, 10) on the top of the test file. I'd like to know why this worked and if there is some better way to fix that?
[EDIT] It looks like this is a node-version specific issue. Test fails on 0.12.4 but runs correctly on 0.10.38 .
[EDIT] Actual code is available here.
Based on your code, I am assuming that your funcB function is running code in a synchronous way.
So when I create funcB in this way:
function funcB() {
return [1, 2, 3];
}
And run the test, Mocha shows an error:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
But if I convert the funcB in asynchronus function as this:
function funcB(cb) {
process.nextTick(function () {
cb(null, [1, 2, 3]);
});
}
Mocha runs the test without any problem:
✓ should return an array
So my complete code that runs ok (the funcB commented is the one that will cause the error) is this:
// install dependencies
// npm install promise
// npm install sync
var Promise = require('promise');
var assert = require('assert');
var Sync = require('sync');
function funcA() {
return new Promise(function (resolve, reject) {
Sync(function () {
return funcB.sync();
}, function (err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
// function funcB() {
// return [1, 2, 3];
// }
function funcB(cb) {
process.nextTick(function () {
cb(null, [1, 2, 3]);
});
}
it("should return an array", function(done) {
return funcA().then(
function (result) {
console.log(result);
assert.equal(Array.isArray(result), true);
done();
}
);
});
So I am opinion I think that the misuse of the sync method (using it on synchronous functions) created by sync library is the one that is causing this problem.
You may just be missing the done() callback:
it("should return array", function(done) {
funcA().then(function(result) {
expect(result).to.be.an.instanceof(Array);
done();
});
});
http://mochajs.org/#asynchronous-code
You can use a timeout parameter for mocha executable.
For example, if you want a 500 milliseconds timeout, just change your package.json to:
"scripts": {
"test": "mocha specs --timeout 500 --require specs/helpers/chai.js"
},
And maybe your promise is being rejected, so you have to call done using catch method.
it("should return array", function(done) {
return funcA().then(function(result) {
expect(result).to.be.an.instanceof(Array);
done();
}).catch(done);
});
This would also help you debug eventual errors that might happen in your promise code.

How to test stubbed database queries

I have a Sails.Js controller that looks like this
module.exports = {
confirmID: function(req,res) {
var uid = req.params.id;
User.findOne({id:uid}).exec(function(err,user) {
// ...
});
}
}
where User is a sails-postgres model. I have tried testing it with mocha, sinon and supertest with a test like this
describe('Controller', function() {
var sandbox;
before(function() {
sandbox = sinon.sandbox.create();
sandbox.stub(User, 'findOne');
});
after(function() {
sandbox.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,res) {
sandbox.fakes[0].called.should.be.true;
done();
});
});
});
If I leave it at that it errors out because exec is called on undefined, but I can't seem to stub the nested exec method without either errors or the test hanging. Is there a way to stub a series of method calls such as .find().exec()? Or am I best to just leave this to integration tests where I can test it with an actual database?
Assuming that you really want to stub (not just spy) - you want to control what the query resolves to as opposed to simply knowing whether the query was executed. Here's what I'm using to stub sails/waterline query methods. Something like...
var stubQueryMethod = require('stubQueryMethod');
describe('Controller', function() {
before(function() {
stubQueryMethod(User, 'findOne', {
id: 123,
name: 'Fred Fakes'
});
});
after(function() {
User.findOne.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,user) {
user.should.have.property('name', 'Fred Fakes');
done();
});
});
});
});
Source: https://gist.github.com/wxactly/f2258078d802923a1a0d
For people looking for other options to stub or mock waterline models, I've found the following four options:
stubQueryMethod.js gist - https://gist.github.com/wxactly/f2258078d802923a1a0d
model mock gist - https://gist.github.com/campbellwmorgan/e305cc36365fa2d052a7
weaselpecker - https://github.com/ottogiron/weaselpecker
sails-mock-models - https://github.com/ryanwilliamquinn/sails-mock-models
After evaluating each one, I've decided on sails-mock-models because it is easy to understand and seems the most used sails mocking library according to npm: https://www.npmjs.com/package/sails-mock-models
Hope this helps someone!
Update: I'm still using sails-mock-models, and it is quite easy, but there are a few drawbacks such as it fails to return promises that are taken into a q.all(promiseArray).then() call. If I get around to investigating the other options or find a workaround, I will post it here.
This will only work for queries that use exec and it overloads all exec calls so if you try to return an error and you have, say, a controller with a policy out front, and the policy does a database lookup, you'll likely go into error there prior to hitting the controller code you intended to test.... that can be fixed with stub.onCall(x), but it is still a bit precarious.
Warnings aside, here's how I've done this in the past:
var path = require('path');
var sinon = require('sinon');
var Deferred = require(path.join(
process.cwd(),
'node_modules/sails',
'node_modules/waterline',
'lib/waterline/query/deferred'
));
module.exports = function () {
return sinon.stub(Deferred.prototype, 'exec');
};
Assuming you have the following service, MyService:
module.exports.dbCall = function (id, cb) {
Model.findOne(id).exec(function (err, result) {
if (err) {
sails.log.error('db calls suck, man');
return cb(err, null);
}
cb(null, result);
});
};
You can test the error case like so:
before(function () {
stub = databaseStub();
});
afterEach(function () {
stub.reset();
});
after(function () {
stub.restore();
});
it('should return errors', function (done) {
stub.onCall(0).callsArgWith(0, 'error');
MyService.dbCall(1, function (err, results) {
assert.equal(err, 'error');
assert.equal(results, null);
done();
});
});

waitForCondition method never returning?

I'm trying to use Intern to author a first functional test, and I can't get around the following problem: when running the following test script
define(["intern!object",
"intern/chai!assert"
], function(registerSuite, assert){
registerSuite({
name: "suite",
"test": function(){
var browser = this.remote;
console.log("HELLO !");
browser.get("http://www.google.com", function() {
console.log("PAGE LOADED !");
browser.waitForCondition("!!window.document.gbqf", 1000, function(err, value){
console.log("LET'S BEGIN... ");
browser.eval("window.document.gbqf", function(err, value){
console.log("BYE BYE !");
browser.quit();
});
});
});
}
});
});
in chrome, using selenium server standalone 2.32.0 and chromedriver 26.0.1383.0 for windows, my test never end and the last message displayed in the console is "PAGE LOADED !".
Does anyone has an idea of how I'm suppose to write this test, or have a link to some proper (real life) functional tests examples ?
Thanks for your help,
Sebastien
The functional interface does not use callback arguments, it uses promises instead. Your code should look like this:
define([
"intern!object",
"intern/chai!assert"
], function (registerSuite, assert) {
registerSuite({
name: "suite",
"test": function () {
var browser = this.remote;
console.log("HELLO !");
return browser
.get("http://www.google.com")
.then(function () {
console.log("PAGE LOADED !");
})
.waitForCondition("!!window.document.gbqf", 1000)
.then(function () {
console.log("LET'S BEGIN... ");
})
.eval("window.document.gbqf")
.then(function (value) {
console.log("BYE BYE !");
});
});
}
});
});
Note in particular:
The last promise chain from this.remote is returned by the test function to indicate to Intern that the test is asynchronous
waitForCondition does not return a value
You should never call quit yourself; this is handled by Intern once the test is complete

Resources