How to mark a jasmine test as failed? - javascript

I have a jasmine 2.0 test that if a function is called, the test failed.
I have a function "Remote.get" that should call the first argument (which is a callback) if it is successful, or the second argument if it failed.
If it calls the second argument, I need to mark the test as failed.
How can I clearly mark the test as failed?
describe("my tests", function() {
it("should call the first function", function(done) {
Remote.get(
function() {
// yeah! good!
done();
},
function() {
// whoa, if we got here, then it didn't work
// fail()!
done();
}
);
});
});
I know I could do something like expect(true).toBe(false) but I the error you get then would be unclear and unrelated to the actual problem. It should give an error like "wrong callback was called" or "Remote.get failure was called". I was hoping there was something more descriptive in Jasmine.
What I'm really looking for is the python equivalent of http://docs.python.org/2/library/unittest.html#unittest.TestCase.fail.

They added a fail() method very recently in this commit. It will likely be released with the next point release of jasmine (assuming 2.1, unclear when this will be), or you can build your own from edge.

You could write and register a custom matcher whose compare function in the return value always fails, and assign your custom message to the message property

Related

What is done(), and where is it documented?

I'm working to understand unit testing in JavaScript, using Mocha/Sinon/Chai. I have seen the function done() used. But I cannot seem to find documentation for this function. It does not seem to be a part of the JavaScript language. If it were, I would expect to see it in the Mozilla documentation under [something].prototype.done(). But it's not there. I don't see it under jQuery's documentation, nor under Mocha's.
On another thread, I see this example of done():
it('should have data.', function () {
db.put(collection, key, json_payload)
.then(function (result) {
result.should.exist;
done();
})
.fail(function (err) {
err.should.not.exist;
done();
})
})
What is done(), what language or tooling is it a part of, and where is the documentation for it? Is done() just a naming convention for a callback function?
Done is a callback that mocha will provided as the first parameter to a unit testing it block. It is usually needed when testing asynchronous code, as it can be call to notify mocha that the it block is completed. It is good practice to name the callback done. However, you can name it as you want.
You can find its documentation here just hit ctrl + f on windows or ⌘ + f on MAC, then enter done.
it('should have data.', function (done) { // inject done here
db.put(collection, key, json_payload)
.then(function (result) {
result.should.exist;
done();
})
.fail(function (err) {
err.should.not.exist;
done();
})
})
Copied the following from mocha website.
Testing asynchronous code with Mocha could not be simpler! Simply invoke the callback when your test is complete. By adding a callback (usually named done) to it(), Mocha will know that it should wait for this function to be called to complete the test. This callback accepts both an Error instance (or subclass thereof) or a falsy value; anything else will cause a failed test

Mocha test timeouts when testing length of array

I'm testing a little node module with mocha and expect.js and I'm having a bit of a headache.
This is the test definition:
it('Should return an expansion array that contains all the objects that comes along within a single group', function (done) {
visibility.expand(data, config.data, companies, groups, function (error, expansion) {
expect(error).to.be(null);
expect(expansion).to.be.an('array');
expect(expansion).to.have.length(2);
expect(expansion.toString()).to.eql([testCompaniesIds[2], testCompaniesIds[3]].toString());
done();
});
});
data is a wrapper for database access and functionality. I initialize it in the before method of my test suite.
config.data holds an object with config params (not relevant)
companies is an array of strings
groups is an array of strings
The problem that i'm facing is that when Mocha reaches this line
expect(expansion).to.have.length(2);
and the length is different from 2, instead of throwing an error like: Expected 2 but length was 1 it simply stops and throw an error because of the timeout.
I verified that the test executed until that line.
A bit more of information: the method that I'm testing receives an array of company names and an array of group names. Each group contains an array of company database id's.
The expected behaviour of the method is to return an array with corresponding company id's merged with the array of company id's belonging to the group object.
Edit 2 due to possible duplicate: I was wrong. It was indeed executing in the scope of a promise.
Edit due to possible duplicate: the expectation in not executing in the scope of a promise (when using promises and executing expect in either resolve or reject functions is the promise who catch the error).
Thanks in advance!
Wrapping all the test between a try-catch like this:
it('Should return an expansion array that contains all the objects that comes along within a single group', function (done) {
visibility.expand(data, config.data, [testCompanies[0].name, testCompanies[1].name], [testGroups[0].name, testGroups[1].name], function (error, expansion) {
try {
expect(error).to.be(null);
expect(expansion).to.be.an('array');
expect(expansion).to.have.length(2);
expect(checkIdIsContainedInArray(testCompaniesIds[0], expansion)).to.be(true);
expect(checkIdIsContainedInArray(testCompaniesIds[1], expansion)).to.be(true);
expect(checkIdIsContainedInArray(testCompaniesIds[2], expansion)).to.be(true);
expect(checkIdIsContainedInArray(testCompaniesIds[3], expansion)).to.be(true);
done();
} catch (e) {
done(e);
}
});
});
This test throws an error due to array length (is 4 and should be 2):
Error: expected [ '464142414441464142414441',
'464142414441464142414442',
'464142414441464142414443',
'464142414441464142414444' ] to have a length of 2 but got 4
Instead of:
Error: timeout of 2000ms exceeded
which can mean anything.
Debugging expect.js I saw that it was throwing an error but Mocha didn't manage to capture it.
Although this solution might not be as elegant as desired, at least it gives the desired feedback instead of a timeout.
You are executing the expectations in a callback function.
This means that the code is being executed in the visibility.expand method.
I'm pretty sure that the behaviour that you are seeing is because of the use of promises... are you using promises in the visibility.expand method?
I believe this is actually working as expected.
The Mocha tests are only able to end the async call when the done() is actually called. Since an error is thrown outside of the scope of the mocha execution context, it never reaches that block of code and the execution basically runs out of time.
I can't seem to find any official documentation stating this, but here's a few (somewhat related hopefully?) references -
https://github.com/pouchdb/pouchdb/issues/1339#issuecomment-34739526
http://chaijs.com/guide/styles/#expect //search around for error handling on callbacks
Is there a way to get Chai working with asynchronous Mocha tests?
Mocha failed assertion causing timeout
Edit - Actually, I'm seeing that I had it backwards, it should be catching the exception for sure. I would relook at the original code again and see if you have everything correct. Maybe update expect.js and see if it has some issues on your current version (or file a ticket). I use chai with the expect library and am able to get it to error fine. http://chaijs.com/
Code example with error being thrown correctly -
var chai = require('chai')
, expect = chai.expect;
describe('something', function () {
var j = [1];
it('does something.. ', function (done) {
setInterval(function () {
console.log('its done');
expect(j).to.be.length(2);
done();
}, 1000);
});
});
//Uncaught AssertionError: expected [ 1 ] to have a length of 2 but got 1

Catching out-of-scope errors with Mocha and Chai

I am modifying a node.js library to support true asynchronous operations.
I am having troubles with Mocha and Chai to make this (a similar) test pass.
it('should throw an error', function() {
expect(function() {
process.nextTick(function() {
throw new Error('This is my error');
});
}).to.throw(Error);
});
The problem is - because of the nextTick - that the Error is thrown out of scope of it and besides the test failing, Mocha also outputs the below.
Uncaught Error: This is my error
What is the proper way to structure this test in order to make it succeed?
Hmm... in a full-fledged application what I'd do is probably use something like Sinon to check that the method that should throw an error has been called and is throwing.
In code where you cannot do this, then the following method would trap the exception:
var expect = require("chai").expect;
var domain = require("domain");
it('should throw an error', function(done) {
var d = domain.create();
d.on('error', function (err) {
// Exit the current domain.
d.exit();
// We must execute this code at the next tick.
process.nextTick(function () {
console.log(err); // Just to show something on the console.
expect(err instanceof Error).to.be.true;
done();
});
});
d.run(function () {
process.nextTick(function() {
throw new Error('This is my error');
});
});
});
This code creates a "domain" stored in d. A domain will emit error events on uncaught exceptions that happen in it so we run the test inside the domain (d.run(...)) we've created and wait for an exception to happen (d.on('error', ...). We check that it is an Error object. (In a real test, I'd also check the error message.) When we are finished we call done() to tell Mocha that the asynchronous test is over.
The handler for error events calls d.exit(). This is to make it so that Mocha can catch the error normally if the assertion (expect(err instanceof Error)...) turns out to fail. If we do not exit the domain, then the domain will trap the error. Also, the check itself must be performed on the next tick to be outside the d domain.
Is Using domain A Problem?
NO!
The documentation for domain comes with some warnings about shutting down operations once an uncaught exception is caught when running an ongoing process, like a server. Then the thing to do is to clean what can be cleaned and exit as soon as possible. However, using domain in a test does not differ from what Mocha is already doing. The way Mocha catches unhandled exceptions in asynchronous code is by using process.on('uncaughtException'. Upon catching an unhandled exception Mocha marks the current test as failed and continues. Yet the documentation regarding uncaughtException says "Don't use it, use domains instead. If you do use it, restart your application after every unhandled exception!"
Ergo, anyone who has a problem with using domain should not be using Mocha in the first place.
You're trying to catch an exception on the incorrect function, the container for the function that throws. Also, because the function is wrapped in nextTick it is executed in a different stack and therefore the exception cannot be captured (this is simply a JS thing unfortunately).
Try this instead:
it ('should throw an error', function (done) {
process.nextTick(function () {
var myFn = function () { throw new Error() };
expect(myFn).to.throw(Error);
// Tell mocha the test is complete
done();
});
});
Update: There is no proper way to structure this test to make it pass as you cannot catch the exception in this scenario. Perhaps update your code to use callbacks to handle errors:
function doSomethingUnsafe() {
try {
// Run code here that may cause exceptions...
callback(null, 'Woohoo! No errors!');
} catch (e) {
callback (e, null);
}
}

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.

how to pass a test with expect.js async done() taking an error?

Mocha website states:
"To make things even easier, the done() callback accepts an error, so we may use this directly: [see their example]"
So lets try that:
it('works',function(done){
expect(1).to.be(1)
done( new Error('expected error') )
})
/* Insert the error manually for testing and clarity. */
run it and:
1 failing
1) works:
Error: expected error
at Context.<anonymous>
[stack trace]
How do we make the test pass when the error response is the desired result?
That's not async. The callback function is just there for you to inform mocha that you're testing async code and so mocha shouldn't run the next test until you call the callback function. This is an example of how to use the callback function:
it('works',function(done){
setTimeout(function(){
// happens 0.5 seconds later:
expect(1).to.be(1);
done(); // this tells mocha to run the next test
},500);
});
Also, mocha does not handle any form of exceptions, async or otherwise. It leaves that up to an exception library of your choosing. In your case you're using expect.js? If so, expect handles expected errors via the throwException method (also called throwError):
it('throws an error',function(done){
expect(function(){
throw new Error('expected error');
}).to.throwError(/expected error/);
});
Now, in general async code in node.js don't throw errors. They pass errors to the callback as parameters instead. So to handle async errors you can simply check the err object:
// using readFile as an example of async code to be tested:
it('returns an error',function(done){
fs.readFile(filename, function (err, data) {
expect(err).to.be.an(Error);
done(); // tell mocha to run next test
})
});
So use to.throwError() if you're checking synchronous errors and to.be.an(Error) if you're checking async errors.
Additional notes:
The first time I saw this I was stumped. How can mocha know that the test is synchronous or asynchronous when the only difference is weather the function you pass to it accepts an argument or not? In case you're like me and are scratching your head wondering how, I learned that all functions in javascript have a length property that describes how many arguments it accepts in its declaration. No, not the arguments.length thing, the function's own length. For example:
function howManyArguments (fn) {
console.log(fn.length);
}
function a () {}
function b (x) {}
function c (x,y,z) {}
howManyArguments(a); // logs 0
howManyArguments(b); // logs 1
howManyArguments(c); // logs 3
howManyArguments(howManyArguments); // logs 1
howManyArguments(function(x,y){}); // logs 2
So mocha basically checks the function's length to determine weather to treat it as a synchronous function or asynchronous function and pauses execution waiting for the done() callback if it's asynchronous.
Even more additional notes:
Mocha, like most other js unit test runners and libraries, work by catching errors. So it expects functions like expect(foo).to.be.an.integer() to throw an error if the assertion fails. That's how mocha communicates with assertion libraries like expect or chai.
Now, as I mentioned above, a common idiom in node is that async functions don't throw errors but passes an error object as the first argument. When this happens mocha cannot detect the error and so can't detect a failing test. The work-around to this is that if you pass the error object from the async code to the callback function it will treat it the same as a thrown error.
So, taking one of my examples above:
it('executes without errors',function(done){
fs.readFile(filename, function (err, data) {
done(err); // if err is undefined or null mocha will treat
// it as a pass but if err is an error object
// mocha treats it as a fail.
})
});
Or simply:
it('executes without errors',function(done){
fs.readFile(filename,done);
});
Strictly speaking, this feature is a bit redundant when used with libraries like expect.js which allows you to manually check the returned error object but it's useful for when your assertion library can't check the error object (or when you don't really care about the result of the async function but just want to know that no errors are thrown).
You can also return your async such as promise as below.
it('Test DNA', () => {
return resolvedPromise.then( (result)=>{
expect(result).to.equal('He is not a your father.');
},(err)=>{
console.log(err);
});
});

Categories