mocha/chai return undefined - node.js

I have a working node script and am now trying to learn mocha/chai to add some unit tests.
//Module Code
var path = require('path');
var recursive = require('recursive-readdir');
function findData(folderPath) {
recursive(folderPath, function (err, files) {
return files;
});
};
module.exports.findData = findData;
My mocha test code:
var expect = require('chai').expect;
describe('checkData', function () {
var findData = require('../custom_modules/findData').findData;
var path;
before (function () {
path = '/Data'
});
it('should have 53 files in array', function () {
expect(findData(path)).to.have.lengthOf(53);
})
});
However, it always fails because the return seems to be undefined. So i stripped my module code back to test a return true and that worked.
So it must the the asynchronous nature of the recursive module so I then tried add in callbacks to my module code:
var path = require('path');
var recursive = require('recursive-readdir');
function findData(folderPath, cb) {
recursive(folderPath, function (err, files) {
cb(null, files);
});
};
module.exports.findData = findData;
But that didn't work either.
The weird thing is if I run node index.js i get the list of files.
Can anyone explain to me how this code can work normally but when I try to use mocha/chai to test I get undefined?
Thanks
EDITED:
So based on what #Louis said in the comments I have added a callback to the function.
describe('checkData', function () {
var findData = require('../custom_modules/findData').findData;
var path;
var files;
before (function () {
path = '/Users/tjmartin/Documents/OSData/OpenNames/Data'
});
it('should have 53 files in array', function () {
expect(findData(path, function(results) {
files = results;
})).to.have.lengthOf(53);
})
});
But this still returns an undefined.

First up, I would log the err result in your findData implementation. Even if only during development, so you can easily see if any errors are reported (you may be doing this already, just wanted to mention it).
As you have spotted, one of the main causes of problems for you is that the callback is asynchronous. So you can't simply return it from your findData method as in your original example.
Next, I wouldn't hard-code the path as you have in the before function. Instead use a local path, so that the test can be run as part of your CI (if you ever have one) or even so you can grab it on another machine and have it work there.
before(function() {
path = './tests/TestData';
});
In your revised example, although you are using a callback you are testing the return result still. You need to alter your test to use the result of the callback.
it('should have 53 files in array', function(done) {
findData(path, function(results) {
expect(results).to.have.lengthOf(53);
done();
});
});

Related

Mocha - Failed looping through array that gets initialized in before()

Consider this test setup:
var expect = require('chai').expect;
var fs = require('fs');
describe('my test', function() {
var dataArr;
// get data
before(function(done) {
fs.readFile('myArray.json', 'utf8', function (err, data) {
dataArr = JSON.parse(data);
done();
});
});
// test data
dataArr.forEach(function(val, index) {
it('Testing element no ' + (index+1), function(done) {
expect(val).to.be.cool;
done();
});
});
});
This test returns the error Cannot read property 'forEach' of undefined because the loop is executed before before().
I found this approach on SO which uses nested its, but that seems to be a dirty workaround (googling mocha nested it proves that).
Isn't there any clean solution for this not so uncommon (?) problem?
Tests are defined synchronously. It is test function that is postponed by asynchronous done call, not dataArr.forEach.
In this case asynchronous readFile provides no benefits. It would be simpler with:
before(function() {
const data = fs.readFileSync('myArray.json', 'utf8');
dataArr = JSON.parse(data);
});
And there's no need for readFile at all. Reading the file isn't a part of a test and should be performed once. Node.js natively supports importing JSON files:
var expect = require('chai').expect;
var dataArr = require('./myArray.json');
You are executing the forEach method out of mocha's hands, you should move all your logic inside it method
So update your code like below:
it('Testing elements', function(done) {
expect(dataArr).all.to.be.cool;
});
I don't think any any cleaner solutions exist.
In your example, dataArr is undefined at the time it is evaluated, that is the member forEach doesn't yet exist because dataArr is not yet an array at that point. It would be defined inside the it()'s callback function scope as the it() callback function is called after the 'before' callback.
So the nested "describe-it" workaround works because it fixes the evaluation order such that dataArr is defined at that point.
So the already mentioned workaround is like this:
var assert = require('assert');
var fs = require('fs');
describe('my test', function() {
var dataArr;
// get data
before(function(done) {
dataArr = ["cool", "cool", "cool"]; // or fs.readFile or require
done();
});
// test it
it('test after data setup', function() {
describe('the tests', function() {
// now the dataArr is defined!
dataArr().forEach(function(val, index) {
it('Testing element no ' + (index+1), function(done) {
assert.equal(val, "cool");
done();
});
});
});
});
});
On the other hand if you don't alter the dataArr, you could evaluate it at once on spot of the declaration like:
var assert = require('assert');
var fs = require('fs');
describe('my test', function() {
const dataArr = ["cool", "cool", "cool"]; // or dataArr = require('testfile.json');
before(function(done) {
done();
});
// test it
// now the dataArr is defined!
dataArr().forEach(function(val, index) {
it('Testing element no ' + (index+1), function(done) {
assert.equal(val, "cool");
done();
});
});
});

Mocha, Sinon and Chai testing two http calls inside a callback

I am doing some really simple testing with Chai Mocha and Sinon. I am wondering how you would go about testing a http method that gets called inside of a callback. Please point me in the right direction if you can, struggling to find anything on it, I know you can do it, just not sure how. My code is below:
index.js
const http = require('http')
class Index {
add(a, b) {
return a + b
}
get(uri) {
http.get(uri, () => {
http.get('/', function() {
return
})
})
}
}
module.exports = Index
index.spec.js
const Index = require('../index')
const http = require('http')
const { stub, fake, assert } = require('sinon')
const { expect } = require('chai')
let httpSpy;
beforeEach(function () {
a = new Index()
httpSpy = stub(http, 'get')
})
describe('When the get method is invoked', function () {
beforeEach(function () {
a.get('http://www.google.co.uk')
})
it('should make a call to the http service with passed in uri', function () {
assert.calledOnce(httpSpy) // This really should be called twice (Part I am struggling with)
assert.calledWith(httpSpy, 'http://www.google.co.uk')
// I want to test instead that the httpSpy was called twice as, inside the get method, when the first http get resolves, another one gets fired off also
})
})
There are two issues.
Firstly, we cannot tell when the Index.get() method execution is ended (it does not accept a callback, neither return a promise, not marked as async etc).
get(uri) { ... }
Usage of such method is critically inconvenient. For example: if we'd want to first do Index.get() and then do some action right after we won't be able to.
To fix the this we can just add a callback as a last parameter of Index.get.
The second issue is how the http.get method is stubbed:
httpSpy = stub(http, 'get')
This line basically means: replace http.get with an empty function. That dummy function won't throw if you pass a callback inside but it won't call it.
That is why http.get is called only once. It simply ignores the passed callback where http.get should be called the second time.
To fix this we can use stub.yields() method to make sinon aware that the last parameter passed to a stub is a callback (and sinon needs to call it). You can find the method in the docs.
Here is a working example, please see my comments:
class Index {
// Added a callback here
get(uri, callback) {
http.get(uri, () => {
http.get('/', () => {
// Pass any data you want to return here
callback(null, {});
})
})
}
}
let httpSpy;
beforeEach(() => {
a = new Index()
// Now sinon will expect a callback as a last parameter and will call it
httpSpy = stub(http, 'get').yields();
})
describe('When the get method is invoked', () => {
const uri = 'http://www.google.co.uk';
// Now we are waiting for the execution to end before any assertions
beforeEach(done => {
a.get(uri, done);
});
it('should make a call to the http service with passed in uri', () => {
assert.calledTwice(httpSpy);
assert.match(httpSpy.getCall(0).args[0], uri);
assert.match(httpSpy.getCall(1).args[0], '/');
});
})

Unit Testing Node Script with chai-spy ... function spied on is undefined

Writing my first node.js test for my first gulp-plugin....using mocha and chai. I get TypeError: Cannot read property 'Assertion' of undefined for ar errorCheckArg = spy(errorCheckArg);. It seems the errorCheckArg function is not available in the testing enviroment(I did a console.log(errorCheckArg) and it showed undefined).
Any idea what I am doing wrong?
describe('gulp-foobar', function() {
var fakeFile;
beforeEach(function() {
var fakeFile = new File({
contents: new Buffer('abufferwiththiscontent')
});
});
describe('get files', function() {
it('should do something', function(done) {
var foo = function() { };
foobar(foo);
var errorCheckArg = spy(errorCheckArg);
expect(errorCheckArg).to.have.been.called.with(arguments);
done();
});
});
});
The Gulp plugin/node script being tested:
function foorbar(callback) {
var destinationFile;
errorCheckArg(arguments);
return through2.obj(function(file, enc, next) {
...
If errorCheckArg is a function inside of foobar, you won't be able to spy on it unless you somehow expose it to the outside world. If it looks like so:
function foobar(callback) {
errorCheckArg(arguments);
.
.
.
function errorCheckArg(args){};
}
you've made it a private function.
If it's important enough to test it in in isolation, then you'll need to expose it somehow, and you'll need to refactor your code.
Without refactoring, your option would be to test errorCheckArg based on its effects on the outputs of foobar. If it were important to me to keep the idiom of just calling a function, that's what I would do.

'then' function is not never getting called by Mocha for assertion checking

I have written the following test case in Mocha, where the my code uses Q module.
var expect = require("chai").expect;
var utils = require("../Utils.js");
var utils1 = require("../a.js");
var sinon = require('sinon');
var request = require('requestretry');
var querySys = require('../b.js');
var Q = require("q");
describe("Sample", function () {
var results;
describe("#get()", function () {
before(function (done) {
done();
});
it("equal", function () {
var deferred = Q.defer();
var responseData = {};
responseData.code = 200;
responseData.data = [{a:1,b:2}];
deferred.resolve(responseData);
//querySys1 method uses Q promises. That is how I stubbed the response.
sinon.stub(querySys, 'querySys1').returns(deferred.promise);
//get function internally calls querySys1. Hence I have stubbed that.
results = utils1.get(specification);
results.then(function (data) {
//Here I do see data coming as {in:1, out:1}. But still the test case is passing when compare it with {}.
console.log(data);
//Ideally, it should have failed. But it is passing.
expect(data).to.be.equal({});
});
});
after(function (done) {
done();
})
});
});
So, if you see, I am trying to do assertion check in results.then part. I have printed the response, which I am receiving. That is coming as expected. But I am intentionally trying to match with wrong value, but test case is still passing.
Since your test does not include a callback, execution runs through the main block and declares the test as passing without waiting for the result of the then function. You need to let mocha know to wait for the callback:
it("equal", function (done) {
...
results.then(function (data) {
console.log(data);
expect(data).to.be.equal({});
return done();
});
});
When I changed results.then to results.done, test started failing as expected. Can someone say whether this is the right approach.

How do I sinon.stub a nested method with a callback?

I need to test a method that includes a sub-method which makes a call to an API server. I’d like to stud this internal sub-method, but I can’t seem to do that. Here’s an example:
var requests = require('./requests.js');
var utilityClass = {
methodCreatesObject: function (callback) {
// Here’s the method I’m trying to stub:
requests.makeCallToAPI(requestObject, function (err, responseFromAPI) {
doSomethingWithResponse(responseFromAPI, function (err, finalObject) {
if (err) {
callback(err, null);
} else {
callback(null, finalObject); // <- Want to test the value of finalObject
}
});
});
}
}
So, my test looks something like this (updated to show loading requests.js before utility.js):
var should = require('should'),
Joi = require('joi'),
sinon = require('sinon'),
requests = require('../lib/modules/requests.js'),
utility = require('../lib/modules/utility.js')
;
// Start my tests:
describe('Method', function () {
before(function () {
var fakeAPIresponse = { ... }
sinon.stub(requests, 'makeCallToAPI').yield(null, fakeAPIresponse);
});
it('should produce a well-formed finalObject', function (done) {
utilityClass.methodCreatesObject(function (err, response) {
if (err) {
done(err);
} else {
response.should.do.this.or.that;
done();
}
});
});
});
As I understand it, .yields() should try to run the first callback it detects in the arguments and feed its own arguments to it (resulting in doSomethingWithResponse(responseFromAPI, function () {...})). However, when running mocha, I’m getting an error indicating that the API server could not be reached, which suggests that the real requests.makeCallToAPI() is being called, and not my stub.
I must be missing something. What am I doing wrong here?
Where are you requiring the request.js? You will need to require request.js before you load up the module you want to test.
Edit 1: Using sinon.js
Here is a gist of what I meant: https://gist.github.com/limianwang/1114249de99c6a189384
Edit 2: Using proxyquire
If you are intending to test simply the utilities without concern of what actually happens within the requests.makeAPICall, you can use something like proxyquire to do the trick. If you are concerned with the actual logic within requests.js, you can use sinon.stub to stub out the actual request.get api.

Resources