Spy could not track on Async function test with Mocha and Sinon - javascript

I have isMember function as below;
function isMember(req, res, next) {
MyService.GetUserAsync(authCookie)
.then(function (user) {
next();
})
.catch(function (err) {
if (err.status === 400) {
return res.redirect("/notAllowed");
}
else {
return next(err);
}
});
}
My test is like below;
beforeEach(function () {
// Overwrite the global timer functions (setTimeout, setInterval) with Sinon fakes
this.clock = sinon.useFakeTimers();
});
afterEach(function () {
// Restore the global timer functions to their native implementations
this.clock.restore();
});
it.only("pass authentication and set authCookie", function (done) {
var user = {
userNameField: "fakeUserName"
};
sinon.stub(MyService, "GetUserAsync").returns(Promise.resolve(user));
var spyCallback = sinon.spy();
var req {};
var res = {};
isMember(req, res, spyCallback);
// Not waiting here!
this.clock.tick(1510);
// spyCallback.called is always false
expect(spyCallback.called).to.equal(true);
done();
});
For some reason this.clock.tick is not working and spyCallback.called is always false.
How can I achieve that my spy will wait until next() is called in isMember function?

The sinon fakeTimers replace the global setTimeout function, but your code does not contain any timeout function.
You can use a setTimeout function to delay the execution of the expectation and then resolve your test by calling done() inside the setTimeout. You can try something like this:
setTimeout(function () {
expect(spyCallback.called).to.equal(true);
done();
}, 0);

You need to put done() inside a callback, because of how the event loop works in javascript. First all synchronous code gets executed and then all pending async tasks are executed.
Mocha has support for promises. If you return a promise from it(), it will be waited. So, you can do something like
it.only("pass authentication and set authCookie", function (done) {
var user = {
userNameField: "fakeUserName"
};
sinon.stub(MyService, "GetUserAsync").returns(Promise.resolve(user));
var spyCallback = sinon.spy();
var req {};
var res = {};
return isMember(req, res, spyCallback).then(function () {
expect(spyCallback.called).to.equal(true);
});
});

Related

How to test a method with timeout after calling another method in sinon

How can I test a property inside a timeout from another called method?
I want to test a property if it's changed inside the setTimeout but using sinons useFakeTimer doesn't seems to work. Or am I missing something?
To illustrate here's my code
const fs = require('fs');
function Afunc (context) {
this.test = context;
}
module.exports = Afunc;
Afunc.prototype.start = function () {
const self = this;
this.readFile(function (error, content) {
setTimeout(function () {
self.test = 'changed';
self.start();
}, 1000);
});
}
Afunc.prototype.readFile = function (callback) {
fs.readFile('./file', function (error, content) {
if (error) {
return callback(error);
}
callback(null, content);
})
}
And here's what I have so far.
describe('Afunc', function () {
let sandbox, clock, afunc;
before(function () {
sandbox = sinon.createSandbox();
});
beforeEach(function () {
clock = sinon.useFakeTimers();
afunc = new Afunc('test');
sandbox.stub(afunc, 'readFile').yieldsAsync(null);
});
afterEach(function () {
clock.restore();
sandbox.restore();
});
it('should change test to `changed`', function () {
afunc.start();
clock.tick(1000);
afunc.test.should.be.equal('changed');
});
});
after the clock.tick check the property test is not changed.
Any help is deeply appreciated! Thanks in advance.
Just change this:
sandbox.stub(afunc, 'readFile').yieldsAsync(null);
...to this:
sandbox.stub(afunc, 'readFile').yields();
...and it should work.
Details
yieldsAsync defers using process.nextTick so the callback passed to readFile wasn't getting called until "all instructions in the current call stack are processed"...which in this case was your test function.
So the callback that changed afunc.test to 'changed' was getting called...but not until after your test completed.

Unit testing logic inside promise callback

I have an ES6 / Aurelia app that I am using jasmine to test. The method I am trying to test looks something like this:
update() {
let vm = this;
vm.getData()
.then((response) => {
vm.processData(response);
});
}
Where this.getData is a function that returns a promise.
My spec file looks something like this:
describe('my service update function', () => {
it('it will call the other functions', () => {
myService = new MyService();
spyOn(myService, 'getData').and.callFake(function() {
return new Promise((resolve) => { resolve(); });
});
spyOn(myService, 'processData').and.callFake(function() { return; });
myService.update();
// this one passes
expect(myService.getData).toHaveBeenCalled();
// this one fails
expect(myService.processData).toHaveBeenCalled();
});
});
I understand why this fails - promises are asynchronous and it hasn't been resolved by the time it hits the expect.
How can I push the promises to resolve from my test so that I can test the code inside the call back?
jsfiddle of failed test: http://jsfiddle.net/yammerade/2aap5u37/6/
I got a workaround running by returning an object that behaves like a promise instead of an actual promise
describe('my service update function', () => {
it('it will call the other functions', () => {
myService = new MyService();
spyOn(myService, 'getData').and.returnValue({
then(callback) {
callback();
}
});
spyOn(myService, 'processData').and.callFake(function() { return; });
myService.update();
// this one passes
expect(myService.getData).toHaveBeenCalled();
// this one fails
expect(myService.processData).toHaveBeenCalled();
});
});
Fiddle here: http://jsfiddle.net/yammerade/9rLrzszm/2/
Is there anything wrong with doing it this way?
it((done) => {
// call done, when you are done
spyOn(myService, 'processData').and.callFake(function() {
expect(myService.processData).toHaveBeenCalled();
done();
});
})

Testing js promises with Mocha, Chai, chaiAsPromised and Sinon

I'm trying to test a function that reads in a file and returns a promise with the file's contents.
function fileContents(){
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, data){
if (err) { reject(err); }
else { resolve(data); }
});
});
}
The unit test for the above
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function () {
let fileContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').returns(function(path, callback) {
callback(null, fileContents);
});
let fileContentsPromise = fileContents();
return fileContentsPromise
.then(data => {
expect(data).to.eventually.equal(fileContents);
});
});
The above test errs with
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
I also tried
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function (done) {
let fileContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').returns(function(path, callback) {
callback(null, fileContents);
});
let fileContentsPromise = fileContents();
fileContentsPromise.then(function(data){
expect(data).to.equal(fileContents);
done();
});
});
and got the same error. The function is working in my local site, but I don't know how to write a test for it. I'm new to js. What am I missing?
There are multiple problems with your code. For instance, you redeclare fileContents in your test and assign it a string value, which of course won't work with doing fileContents() in the same test. I'm going to concentrate on two conceptual problems rather than the "duh"-type mistakes like this one.
The two conceptual problems are:
To have fs.readFile call your callback with fake values you must use .yields. Using .returns changes the return value, which you do not use. So stub it like this:
sinon.stub(fs, 'readFile').yields(null, fakeContents);
You are using the .eventually functionality provided by chai-as-promised on a non-promise, but you have to use it on a promise for it to work properly so your test should be:
return expect(fileContentsPromise).to.eventually.equal(fakeContents);
Here's code that works:
const sinon = require("sinon");
const fs = require("fs");
const chai = require("chai");
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const expect = chai.expect;
// We need to have filename defined somewhere...
const filename = "foo";
function fileContents(){
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, data){
if (err) { reject(err); }
else { resolve(data); }
});
});
}
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function () {
let fakeContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').yields(null, fakeContents);
let fileContentsPromise = fileContents();
return expect(fileContentsPromise).to.eventually.equal(fakeContents);
});
});

Node.js How to do conditional Callback?

I am still struggling with the nested callback structure of Node.js. I have looked as async, co and other methods, but they do not seem to help.
What is the best practice on how to code, e.g.
var db = MongoClient.connect(url, callback1 () {
if (auth) }
db.authenticate(user, pswd, callback2 () {
--- should continue with db.collection.find (authenticated)
)
--- should continue with db.collection.find (non-authenticated)
}
So the question ist: How should I code this sequence to be able to execute the db calls following db.connect or db.authenticate (and both callbacks are completed)? The only way I can think of is to have the following db-calls in a separate function and call this function in both callback routines. Not really elegant ...
If what you are confused by is how to put optional conditions before a callback, using async you can do:
var async = require('async');
var db = MongoClient.connect(url, () => {
async.series([
(callback) => {
//Branching
if(auth) {
// Do conditional execution
db.authenticate(user, pswd, () => {
callback();
});
} else {
// Skip to the next step
callback();
}
},
(callback) => {
// Whatever happened in the previous function, we get here and can call the db
db.collection.find();
}
]);
});
I'm not sure that I fully understand what you are asking, by the way, if you want to run a callback depending on some conditions (eg: requires or not authentication)... you can use promises:
var db = MongoClient.connect(url, callback1 () {
if (auth) }
db.authenticate(user, pswd, callback2 () {
--- should continue with db.collection.find (authenticated)
)
--- should continue with db.collection.find (non-authenticated)
}
var MongoClientConnect = (url, username, password) => new Promise((resolve, reject) => {
var db = MongoClient
.connect(url, () => {
if(!requiresAuthentication) {
return resolve(db);
}
db.authenticate(username, password, () => {
//check if login success and
resolve(db);
});
})
;
});
// THEN
MongoClientConnect()
.then(db => db.collection.find("bla bla bla"))
;

how should i do asynchronous unit testing?

I am using pg-promise. i am learnner please excuse if it seems trivial to you. how can i wrtie unit test for. it errors out data is undefined. i have been making connection in js file and export that module.another js file use to query against database and fetch result set. code is working as expected having issue how can i write unit test with mocha and chai.
test1.js
var dbConn= pgp(connUrl);
module.exports = {
getconnect: function () {
return dbConn;
}
};
test2.js
module.exports = {
getData: function (req, res) {
db.getconnect().query(sqlStr, true)
.then(function (data) {
console.log("DATA:", data);
return data;
} } }
unittest.js
describe("Test Cases", function (done) {
it('retrieve response', function (done) {
var req = {};
var res = {};
test2.getData(req, res);
// how would i retrieve value of data from test2.js so i can test
done();
});
});
how would i retrieve "data" value from test2.js inthe unittest.js
Your getData must return the promise. Client code will be able to recognize the moment it's finished(resolved).
module.exports = {
getData: function (req, res) {
return db.getconnect().query(sqlStr, true)
.then(function (data) {
console.log("DATA:", data);
return data;
} } }
test:
describe("Test Cases", function () {
it('retrieve response', function (done) {
var req = {};
var res = {};
test2.getData(req, res).then(function(data){
// test of data returned
done(); // finish test
}).catch(done);// report about error happened
});
});
If you don't need to any process of data in your module, you can to remove whole .then section without any functionality changes.
But if you want to preprocess data - don't forget to return it from each chained .then.
If your test library requires stubs for async stuff, you can use async/await feature to deal with it.
it('retrieve response', async function(){
try {
var data = await test2.getData(req, res);
// test data here
} catch (e) {
// trigger test failed here
}
});
Or stub it, something like this:
var dbStub = sinon.stub(db, 'getConnect');
dbStub.yields(null, {query: function(){/*...*/}});
If you have a function returning promise then you can use await in your tests:
describe("Test Cases", function (done) {
it('retrieve response', async function (done) {
try {
var data = await test2.getData();
// check data constraints ...
...
} catch(err) {
...
}
done(); // finish test
});
});

Categories

Resources