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

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.

Related

How can I mock a function/property of a node module exported as a function using sinon?

I have a service module that is exported as a function. I need to pass a couple of things into it, like a configuration object so it does need to retain this structure. I am trying to stub out a function from the service but can't figure it out. In my app, I have a function that makes an API call that is problematic during testing so I'd like to stub it. (I understand I'd have to write my test differently to handle the async issue)
// myService.js
module.exports = function(config) {
function foo() {
returns 'bar';
}
return {
foo: foo
};
};
// test.js
var config = require('../../config');
var request = require('supertest');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var myService = require('./myService.js')(config);
describe('Simple test', function(done) {
it('should expect "something else", function(done) {
var stub = sinon.stub(myService, 'foo').returns('something else');
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
stub.restore();
})
.end(done);
});
});
* /testRoute I set up as a simple GET route that simply returns the value from myService.foo()
The above is not working, and I believe it has to do with the way my service is exporting. If I write the service as below, the stub works fine.
module.exports = {
test: function() {
return 'something';
}
};
But again, I need to be able to pass in information to the module so I would like to keep my modules in the original structure above. Is there a way to stub a function from a module that exports in that manner? I was also looking into proxyquire but not sure if that is the answer.
The reason why your test stub does not work is that the foo function is created every time the module initializer is called. As you discovered, when you have a static method on the module, then you are able to stub.
There are a variety of solutions to this problem-- but the simplest is to expose the method statically.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function foo() {
return 'bar'
}
It's ugly, but works.
What if the foo function has a closure to variables within the service (which is why it lives within the service initializer). Then unfortunately these need to be explicitly passed in.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function(config) {
return function foo() {
return config.bar;
}
}
Now you can safely stub the module.
However, how you are stubbing should be considered unsafe. Only if your test works perfectly does the stub get cleaned up. You should always stub within the before and after (or beforeEach and afterEach) fixtures, such as:
// We are not configuring the module, so the call with config is not needed
var myService = require('./myService.js');
describe('Simple test', function(done) {
beforeEach(function () {
// First example, above
this.myStub = sinon.stub(myService, foo).returns('something else');
// Second example, above
this.myStub = sinon.stub(myService, foo).returns(function () {
returns 'something else';
});
});
afterEach(function () {
this.myStub.restore();
});
it('should expect "something else", function(done) {
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
})
.end(done);
});
});
There are other options to be able to stub dependencies using dependency injection. I recommend you look at https://github.com/vkarpov15/wagner-core or my own https://github.com/CaptEmulation/service-builder

mocha/chai return undefined

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

'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.

Is there any way to mock this function with Jasmine or Sinon?

I'm testing a module, and I want to mock out a a dependency within that module. Let me frame my scenario, if I may:
In my module
myModule.prototype.func = function(callback) {
complexObj.doStuff('foo', function(err) {
callback(err, 'stuff');
});
};
So, I'm trying to basically mock complexObj. It doesn't really matter if I mock the entire object or just the doStuff function in this case. Let's assume that doStuff does something like interact with a web service or the filesystem. complexObj is being injected into myModule by dependency injection. I've been using Jasmine and Sinon to try to mock or stub this object and function, but I've had no luck, so I've resorted to something like this, which seems a little kludgy:
In my spec:
describe('Testing myModule', function() {
it('should do stuff', function() {
ComplexObj.prototype.doStuff = function(arg, callback) {
callback(null); // If no errors, 'doStuff' returns null indicating no errors
};
var complexObj = new ComplexObj();
new myModule(complexObj).func(function(err, results) {
// Set up expectations...
});
});
});
So, as you can see, I'm psuedo-mocking out the doStuff function in the ComplexObj object. Since I'm not concerned about ComplexObj or doStuff function, I'm just invoking the callback with 'null' indicating to func that there were no errors in doStuff. As I mentioned before, I feel there should be a better way to handle this? Suggestions?
With Jasmine, you would do something like this:
var complexObj = {doStuff: null};
spyOn(complexObj, 'doStuff');
new myModule(complexObj).func(function(err, results) {
expect(complexObj.doStuff).toHaveBeenCalledWith(args, callback);
});
Edit: Or you could set up expectations in your mocked doStuff:
var complexObj = {doStuff: null};
spyOn(complexObj, 'doStuff').andCallFake(function(args, callback) {
expect(args).toEqual(/*...*/);
expect(callback).toEqual(/*...*/);
callback();
});
new myModule(complexObj).func(function(err, results) {
expect(complexObj.doStuff).toHaveBeenCalled();
});

Resources