should.js not causing mocha test to fail - javascript

I'm very new to unit tests, mocha, and should.js, and I'm trying to write a test for an asynchronous method that returns a promise. Here is my test code:
var should = require("should"),
tideRetriever = require("../tide-retriever"),
moment = require("moment"),
timeFormat = "YYYY-MM-DD-HH:mm:ss",
from = moment("2013-03-06T00:00:00", timeFormat),
to = moment("2013-03-12T23:59:00", timeFormat),
expectedCount = 300;
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function() {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
},
function(err) { // reject
should.fail("Promise rejected", err);
}
);
});
});
When I manually test the tideRetriever.get method, it consistently resolves an array of 27 elements (as expected), but the test will not fail regardless of the value of expectedCount. Here is my simple manual test:
tideRetriever.get(from, to).then(
function(entries) {
console.log(entries, entries.length);
},
function(err) {
console.log("Promise rejected", err);
}
);
I can also post the source for the module being tested if it's necessary.
Am I misunderstanding something about Mocha or should.js? Any help would be greatly appreciated.

UPDATE
At some point Mocha started to support returning Promise from test instead of adding done() callbacks. Original answer still works, but test looks much cleaner with this approach:
it("should retrieve and parse tide CSV data", function() {
return tideRetriever.get(from, to).then(
function(entries) {
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
}
);
});
Check out this gist for complete example.
ORIGINAL
CAUTION. Accepted answer works only with normal asynchronous code, not with Promises (which author uses).
Difference is that exceptions thrown from Promise callbacks can't be caught by application (in our case Mocha) and therefore test will fail by timeout and not by an actual assertion. The assertion can be logged or not depending on Promise implementation. See more information about this at when documentation.
To properly handle this with Promises you should pass err object to the done() callback instead of throwing it. You can do it by using Promise.catch() method (not in onRejection() callback of Promise.then(), because it doesn't catch exceptions from onFulfilment() callback of the same method). See example below:
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function(done) {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
done(); // test passes
},
function(err) { // reject
done(err); // Promise rejected
}
).catch(function (err) {
done(err); // should throwed assertion
});
});
});
PS done() callback is used in three places to cover all possible cases. However onRejection() callback can be completely removed if you don't need any special logic inside it. Promise.catch() will handle rejections also in this case.

When testing asynchronous code, you need to tell Mocha when the test is complete (regardless of whether it passed or failed). This is done by specifying an argument to the test function, which Mocha populates with a done function. So your code might look like this:
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function(done) {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
done();
},
function(err) { // reject
should.fail("Promise rejected", err);
done();
}
);
});
});
Note that the way Mocha knows this is an async test and it needs to wait until done() is called is just by specifying that argument.
Also, if your promise has a "completed" handler, which fires both on success and failure, you can alternatively call done() in that, thus saving a call.
More info at:
http://mochajs.github.io/mocha/#asynchronous-code

Related

Test callback invocation at the end of promise chain

I am dealing with a code mixing node-style callbacks and Bluebird promises, and I need to write some unit tests for it.
In particular, cache.js exposes the init() function, which works with promises. It is then called by the doSomething() function in another file (e.g. index.js) which in turn accepts a callback that has to be invoked at the end of init().
Pseudocode is as follows:
// [ cache.js ]
function init() {
return performInitialisation()
.then((result) => return result);
}
// [ index.js ]
var cache = require('./cache');
function doSomething(callback) {
console.log('Enter');
cache.init()
.then(() => {
console.log('Invoking callback');
callback(null);
})
.catch((err) => {
console.log('Invoking callback with error');
callback(err);
});
console.log('Exit');
}
A possible unit test could be (showing only relevant code):
// [ index.test.js ]
...
var mockCache = sinon.mock(cache);
...
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
var callback = sinon.spy();
doSomething(callback);
expect(callback).to.have.been.calledOnce;
done();
});
This test passes, however changing the expectation to not.have.been.calledOnce also passes, which is wrong.
Also, console logs are out of sequence:
Enter
Exit
Invoking callback
I have looked at several possibilities, none of which worked:
Using chai-as-promised, e.g. expect(callback).to.eventually.have.been.calledOnce;
Refactoring doSomething() to be simply:
function doSomething(callback) {
cache.init()
.asCallback(callback);
}
Can anyone help me understand what I am doing wrong and how I can fix it please?
console logs are out of sequence
The logs are in the correct order because your Promise will be async meaning, at the very least, the internal console logs calls in then & catch will run on the next tick.
As to why the test is failing is the result of a couple of issues, first one is you don't appear to have sinon-chai configured correctly, or at best your calledOnce assertion isn't kicking in. Just to confirm, the top of your test file should something like:
const chai = require("chai");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
If you have that and it's still not working correctly then might be worth opening an issue on the sinon-chai lib, however, a simple workaround is to switch to sinon assertions e.g.
sinon.assert.calledOnce(callback)
Secondly, when you do eventually fix this, you'll probably find that the test will now fail...everytime. Reason being you've got the same problem in your test that you have with your logging - your asserting before the internal promise has had a chance to resolve. Simplest way of fixing this is actually using your done handler from Mocha as your assertion
mockCache.expects('init').resolves({});
doSomething(() => done());
In other words, if done get's called then you know the callback has been called :)
Following James' comment I revisited my tests like this:
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
doSomething(done);
});
it('calls the callback on error', function(done) {
mockCache.expects('init')
.rejects('Error');
doSomething((err) => {
if (err === 'Error') {
done();
} else {
done(err);
}
});
});

Using promises to wait for function to finish

I've read some tutorials online for the Promises method but I'm still a bit confused.
I have a Node app.js which performs several functions including connecting to a db.
db.connect(function(err) {
setupServer();
if(err) {
logger.raiseAlarmFatal(logger.alarmId.INIT,null,'An error occurred while connecting to db.', err);
return;
}
Now I have written a mocha unit test suite, which encapsulates this app and performs several request calls to it. In some cases what occurs is that the the test initializes without confirmation that the db has successfully connected i.e: setupServer() has been performed.
How would I implement the promises method to this bit of asynchronous code, and if not promises, what should I use ? I have already tried event emitter but this still does not satisfy all the requirements and causes failures during cleanup.
If you're using mocha, you should use asynchronous code approach. This way you can instruct mocha to wait for you to call done function before it goes on with the rest.
This would get you started:
describe('my test', function() {
before(function(done) {
db.connect(function(err) {
setupServer(done);
});
})
it('should do some testing', function() {
// This test is run AFTER 'before' function has finished
// i.e. after setupServer has called done function
});
});
assuming that your setupServer calls the done function when it's done:
function setupServer(done) {
// do what I need to do
done();
}
You will need to use Promise inside the body of function that has async work. For your case, I think that is setupServer() which you said contains ajax requests.
conts setupServer = () => {
return new Promise((resolve, reject) => {
//async work
//get requests and post requests
if (true)
resolve(result); //call this when you are sure all work including async has been successfully completed.
else
reject(error); //call this when there has been an error
});
}
setupServer().then(result => {
//...
//this will run when promise is resolved
}, error => {
//...
//this will run when promise is rejected
});
For further reading:
Promise - MDN

Jasmine Node javascript promise testing

I want to test some promises with jasmine node. However, the test runs but it says that there are 0 assertions. This is my code, is there something wrong? The then part is successfully called, so if I have a console.log there, it gets called. If I have the code test a http request, on the success, the assertion is correctly interpretated.
describe('Unit tests', function () {
it("contains spec with an expectation", function() {
service.getAllClients().then(function (res) {
expect("hello world").toEqual("hello world");
done();
}).catch(function (err) {
fail();
});
});
});
You need to specify the done argument to the callback you pass to it, so Jasmine knows you are testing something asynchronously:
it("contains spec with an expectation", function(done) {
// ...
When you include that parameter, Jasmine will wait for a while for you to call done so it knows when you're done.
done();
Secondly, in an asynchronous test, it probably is better to fail with a call of done.fail:
done.fail();

Can you nest it() blocks in a request callback?

It seems that you could put "stuff" inside describe but outside it:
describe('1', function() {
var foo = 'bar';
console.log(foo);
it('1a', function(done) {
expect(true).toBe(true);
done()
});
it('1b', function(done) {
expect(true).toBe(true);
done();
});
});
And it seems that you could put the it blocks inside of a function that is called:
describe('1', function() {
(function() {
it('1a', function(done) {
expect(true).toBe(true);
done()
});
it('1b', function(done) {
expect(true).toBe(true);
done();
});
})()
});
In both cases the output is:
~/code/study-buddies $ jasmine-node server
bar
..
Finished in 0.005 seconds
2 tests, 2 assertions, 0 failures, 0 skipped
But when I try to put my it blocks inside of a Node request callback, it doesn't seem to run the tests:
describe('1', function cb1() {
var foo = 'bar';
console.log(foo);
var request = require('request');
request.get('http://localhost:3000/api/posts', function() {
console.log('here');
it('1a', function cb1a(done) {
console.log('1a');
expect(true).toBe(true);
done();
});
it('1b', function cb1b(done) {
console.log('1b');
expect(true).toBe(true);
done();
});
});
});
Output:
~/code/study-buddies $ jasmine-node server
bar
Finished in 0.007 seconds
0 tests, 0 assertions, 0 failures, 0 skipped
here
When I run my real code:
var request = require('request');
var base_url = 'http://localhost:3000/api/posts';
describe('Posts', function() {
var post;
beforeEach(function(done) {
var options = {
url: base_url,
method: 'POST',
json: {
title: 'one'
}
};
request(options, function(err, response, body) {
if (err) console.log('ERROR IN THE SET UP.');
else { post = body; }
done();
});
});
afterEach(function(done) {
request.del(base_url+'/'+post._id, function(err, response, body) {
if (err) console.log('ERROR IN THE TEAR DOWN.');
else { post = null; }
});
done();
});
describe('GET /', function() {
it('returns a status code of 200', function(done) {
request.get(base_url, function(err, response, body) {
expect(response.statusCode).toBe(200);
done();
});
});
it('returns the right posts', function(done) {
request.get(base_url, function(err, response, body) {
expect(JSON.parse(body)).toEqual([post]);
done();
});
});
});
describe('GET /:id', function() {
it('returns a status code of 200', function(done) {
request.get(base_url+'/'+post._id, function(err, response, body) {
expect(response.statusCode).toBe(200);
done();
});
});
it('returns the right post', function(done) {
request.get(base_url+'/'+post._id, function(err, response, body) {
expect(JSON.parse(body)).toEqual(post);
done();
});
});
});
describe('POST /', function() {
var options = {
url: base_url,
method: 'POST',
json: {
title: 'two'
}
};
request(options, function(err, response, body) {
it('returns a status code of 201', function(done) {
expect(response.statusCode).toBe(201);
done();
});
it('returns the created Post', function(done) {
expect(body).toEqual({title: 'two'});
done();
});
});
});
describe('PUT /:id', function() {
});
describe('DELETE /:id', function() {
});
});
I get this no output:
~/code/study-buddies $ jasmine-node server
~/code/study-buddies $
Why is this?
Note: I'm trying to nest the it blocks under the request because both it blocks are making the same request, so I want to be DRY.
There are two steps here:
Collect tests (calls to it()).
Execute collected tests.
In your first example your tests are defined, while handling step 1. In your second example (with callback) you calls to it are executed during step 2 and therefore they are not handled by Jasmine.
How to handle
You need either define separate tests or use only calls to expect() inside your callback without calls to it().
Usually you want to define separate test if you need to send request to the same route, but with different parameters. Typical example is to test API behavior with valid and invalid data:
describe('POST /users/', function() {
it('should create user', function (done) {
request.post('/users/', { /* valid data */ }, function () { /* expect clauses */ });
});
it('should respond with errors given invalid data', function (done) {
request.post('/users/', { /* invalid data */ }, function () { /* expect clauses */ });
});
});
You want to use multiple expect() statements inside single test, when you want to test different parts of a single request. For example check response code and values of few parameters:
describe('GET /users/{id}/', function() {
it('should return user', function (done) {
request.get('/users/14/', function (err, response, body) {
expect(response.code).toBe(200);
expect(body.username).toBe('test');
expect(body.email).toBe('test#example.com');
});
});
});
Obviously there are some in-between cases and that's up to you to decide whether each concrete case is important enough to be put into separate test (and duplicate the same request) or can be handle by additional expect() statements inside single test.
In depth explanation
This requires some background knowledge from reader:
understand difference between synchronous & asynchronous
know what is event loop in Node.js and how it works
understand that function, which accepts another function is not necessary asynchronous (uses event loop for callback) (like forEach for example)
Some facts before continue:
Jasmine expects all test cases to be defined synchronously. After call to main describe() in file is completed it starts execution of collected tests.
Calls to describe() and it() are synchronous, while execution of HTTP requests are asynchronous (uses event loop).
describe() creates a namespace for tests and synchronously calls function provided as second argument. it() adds it's second argument as a test to current namespace.
Let's trace your first case:
Call to describe() creates namespace 1 and synchronously executes cb1().
Call to it() synchronously adds test 1a.
Call to it() synchronously adds test 1b.
Execution of cb1() is completed, as well as test collection step.
Start execution of collected tests.
Execute tests.
Print results.
Exit program.
Addition of IIFE doesn't change anything, since it's just calls itself synchronously.
Let's trace your second case:
Call to describe() creates namespace 1 and synchronously executes cb1().
Send request, put callback to the event loop queue (remember HTTP requests are asynchronous?) and continue execution on the next line.
Execution of cb1() is completed, as well as test collection step.
Start execution of collected tests.
Since there are not tests collected, print 0 tests executed.
Execution of the current call stack is finished. So take next function from the event loop queue, which is request callback.
Call to it() tries to add test 1a, but execution of tests is already completed.
Exit program.
At this point it should be clear that you can't and shouldn't define tests inside asynchronous callbacks. So your third example should never be written. And question why it completes without any output should never be asked.
But just out of the interest I looked at the Jasmine source code and did some debugging to see what actually happened. Here you go.
You probably noticed that functions describe() and it() aren't imported by you, but provided by Jasmine itself. Besides importing these functions for you Jasmine also provides some internal state used by it() and describe() to collect tests. While importing tests this internal state is set, but while running it's not.
When you call it() from the request callback you do this at the stage of running tests and internal set is not set. So it fails with Error: jasmine.Suite() required error. This error causes Jasmine to exit immediately without any output.
You will probably ask why does it print results in the second example then? That's easy: in your second example you don't have any other tests, so at the moment of call to it() results are already printed. You can check it by adding another console.log() call between calls to it() (it will never be printed).

Mocha: assertions within promise not working

So, I'm completely new to mocha, and promises and I was asked to write integration tests, which posts form data to a page, gets an authentication key, and then run tests on the key.
I'm getting the key back properly, which is confirmed by my log statement, but when I run mocha, my test says 0 passing. It doesn't even print out the descriptions for the 'describe' and 'it' statements, implying that they do not work within the promise. Is there away that I can this work, while still giving the assertions access to my authorization key?
require('should');
require('sails');
var Promise = require("promise");
var request = require('request');
describe('Domain Model', function(){
var options = { url:'link',
form:{
username: 'xxxxx#xxxxxx',
password: '0000'
},
timeout: 2000
};
//request auth key
var promise = new Promise(function(resolve,reject){
request.post(options, function(error, response, body){
//body contains the auth key, within a string of JSON
resolve(body);
});
});
//this is where I'm getting lost
promise.then(function(data,test){
var token = (JSON.parse(data))["access_token"];
console.log(token);
describe('Initialize',function(){
it('should be an string', function(){
(token).should.be.type('string');
});
});
});
});
Mocha is not able to find your tests because you're not structuring them correctly. The it() blocks should go immediately within the describe() blocks (and the describe() blocks may be nested within each other as you see fit). If you want to use promises, the promises should go inside the it() blocks, not the other way around. So, as a first pass, the updated code should look something like this (I'm cutting some parts out for brevity):
describe('Domain Model', function(){
var options = { ... };
describe('Initialize',function(){
it('should be an string', function(){
//request auth key
var promise = new Promise(function(resolve,reject){
// ...
});
promise.then(function(data,test){
var token = (JSON.parse(data))["access_token"];
console.log(token);
(token).should.be.type('string');
});
});
});
});
Note that the promise is now contained inside the body of the it(). Mocha should now be able to find your test. However, it won't pass. Why not?
As you probably know, promises are asynchronous. Testing asynchronous code using mocha requires using the done callback - see the guide for more info. Basically, this means the it() block should accept a parameter named done (the name is important!). This done thingy is something that mocha passes in automatically - its presence indicates to mocha that this test contains asynchronous code, and that therefore it has not completed executing until you say so. The way you indicate that your test is done is by executing done, because done is in fact a callback function. You should execute done() at whatever point your test is deemed complete - i.e., at the end of whatever code block is supposed to run last, which in your case is the bottom of the .then() handler for the promise. So, improving upon our last version, the code now looks like this (again, cutting some parts for brevity):
describe('Domain Model', function(){
var options = { ... };
describe('Initialize',function(){
it('should be an string', function(done){
//request auth key
var promise = new Promise(function(resolve,reject){
// ...
});
promise.then(function(data,test){
var token = (JSON.parse(data))["access_token"];
console.log(token);
(token).should.be.type('string');
done();
});
});
});
});
Note that I just added the done parameter to it(), and then I called it at the bottom of the then().
At this point, the code should work, I think... I'm not sure as I'm not able to test it. However, there are a couple more things we could do to improve upon this further.
First, I question your use of promises here. If you had an API for obtaining the access token, then I would opt to have that API return a promise because promises are very convenient for the caller. However, as I'm sure you've noticed, constructing promises can be a bit tedious, and I don't think it adds much value for your code. I would opt to just do this:
describe('Domain Model', function(){
var options = { ... };
describe('Initialize',function(){
it('should be an string', function(done){
//request auth key
request.post(options, function(error, response, body){
//body contains the auth key, within a string of JSON
var token = (JSON.parse(body))["access_token"];
console.log(token);
(token).should.be.type('string');
done();
});
});
});
});
Isn't that so much shorter and sweeter? The code is still asynchronous, so you should still make sure your it() block accepts a done callback, and you should call it when your test has finished.
Now, if you still insist on using promises, then there's one more thing I have to warn you about. What happens if there's an error inside of your .then() handler code? Well, according to the documentation:
If the handler that is called throws an exception then the promise
returned by .then is rejected with that exception.
Do you have a rejection handler for your promise? No. So what does that mean? That means the error will get swallowed up silently. Your test will fail with Error: timeout of 2000ms exceeded, which is because the done handler never got called, but the actual cause of the error won't be shown, and you'll be pulling your hair out trying to figure out what went wrong.
So what can you do? You could use the second parameter to .then() to specify a rejection handler, and in there, you could take advantage of the fact that the done callback that mocha passes in to your test accepts an error argument, so if you call done("something"), your test will fail (which is what we want in this case), and the "something" will be the reason. So here's what that will look like in your case:
describe('Domain Model', function(){
var options = { ... };
describe('Initialize',function(){
it('should be an string', function(done){
//request auth key
var promise = new Promise(function(resolve,reject){
// ...
});
promise.then(function(data){
var token = (JSON.parse(data))["access_token"];
console.log(token);
(token).should.be.type('string');
done();
}, function (err) {
done(err);
});
});
});
});
However, we can do even better. Consider what happens if an error gets thrown from within the rejection handler. Not likely, since you're not doing a whole lot in there - just calling done(err). But what if it does happen? Well, pretty much the same thing - the error will get silently swallowed up, the test will fail with a non-specific timeout error, and you'll be pulling out your hair again. Is there a way we can get that error to bubble up and get rethrown?
As a matter of fact, there is: Both Q and the promise library that you're using have an alternate handler called .done() (not to be confused with mocha's done callback). It's similar to .then(), but with a slightly different behavior around uncaught exceptions. From the documentation:
Promise#done(onFulfilled, onRejected)
The same semantics as .then except that it does not return a promise
and any exceptions are re-thrown so that they can be logged (crashing
the application in non-browser environments)
Perfect, that's exactly what we want. Just be sure to understand when you should use .then(), vs when you should use .done(). The Q API does a fantastic job of explaining (and the promise library you're using has similar behavior - I tested):
The Golden Rule of done vs. then usage is: either return your promise
to someone else, or if the chain ends with you, call done to terminate
it. Terminating with catch is not sufficient because the catch handler
may itself throw an error.
(Note: .catch() appears to be Q-specific, but it's pretty similar to the onRejected callback, which is the second parameter to .then().).
So with that in mind, just replace your last .then() with a .done(). When you use .done(), you can just omit the rejection handler and rely on the promise library to re-throw any unhandled expections, so you will get an error description and a stack trace. With the above in mind, your code now looks like:
describe('Domain Model', function(){
var options = { ... };
describe('Initialize',function(){
it('should be an string', function(done){
//request auth key
var promise = new Promise(function(resolve,reject){
// ...
});
promise.done(function(data){
var token = (JSON.parse(data))["access_token"];
console.log(token);
(token).should.be.type('string');
done();
});
});
});
});
Basically, ignoring the previous code sample, the only difference from the one before that is we're using .done() instead of .then().
Hopefully, this covers most of what you need to get started. There are other things you might want to consider, like potentially retrieving the auth key inside of a before() hook instead of an it() block (because I'm assuming the real thing you're testing is not the retrieval of the key - that's just a prerequisite to test the things you actually want to test, so a hook might be more appropriate - see here). I also question whether or not you should be connecting to an external system from within your tests at all, versus just stubbing it out (that depends on whether these are unit tests or integration tests). And I'm sure you can come up with a better assertion that just checking that token is a string, like using a regex to make sure it matches a pattern, or actually testing a request for a protected resource and making sure that it goes through. But I'll leave those questions for you to think about.

Categories