Jasmine, or Mocha?

So, the story goes like this.

We have a big codebase, and a clean slate to write tests. We had Jasmine tests in the past, but those became unmaintained and stale. So, we got rid of it all, and started afresh.

We figured, since Jasmine worked back then, it’ll probably work now too. Enter, tests in Jasmine. The tests worked seamlessly - they automatically ran when the code changed, came with everyone’s favourite expect syntax, supported the istanbul reporter, and had spies, mocks and stubs built in. What could possibly go wrong?

It does not do to be satisfied when something is just good. Satisfaction is only when it is the best. So, it was time to go hunt for other frameworks, to find out if we couldn’t get better. Some digging around, said that the other big fish in the testing world, was Mocha.

Mocha didn’t seem all that great. I mean, it didn’t even come with an assertion library built in, or spies. The syntax wasn’t really that different, if you went with an expect assertion library, like chai. Which is what I did.

expect(group.validFrom).toEqual('2016-01-22T19:00:00+00:00');

became

expect(group.validFrom).to.equal('2016-01-22T19:00:00+00:00');

Simply from a personal preference, I preferred mocha-chai’s assertion style. Then again, people might prefer the first style, so this is down to personal choice. Pick what you want.

Okay, let’s go further. What about those spies.

Now, mocha doesn’t come with a built in spy framework. So, in comes the king of spying, sinon. I expected it to be a roundabout way of doing things, but oddly, adding sinon, actually simplified the code.

this.spy = spyOn(countryDB, 'isValidCountry').andCallThrough();
it('should have been called', function(done){
expect(this.spy.callCount).toEqual(0);
done();
})

became

sinon.spy(countryDB, 'isValidCountry');
it('should have been called', function(){
expect(countryDB.isValidCountry.notCalled);
})

No matter how you slice it, that definitely is simpler. And while we’re on the topic of simpler, let’s talk about that done function.

On Jasmine, the done function HAS to be called for every single test, else the test fails. While this is fine normally, when working with promises, it gets irritating. If there is a failure, the promise’s .then block isn’t invoked, and so, done isn’t called. Which means the test fails with a generic ‘Timeout exceeded’, instead of the actual error of the promise. The only way around it, was to include a mandatory .finally block, that calls done. You still don’t get the error of the promise in your reporter of choice.

With mocha, that was the end of a huge headache. Just return the promise, and you’re set. If the promise fails, the test fails, and not with a generic message. It worked like you’d intuitively expect it to. The done function was made to make async testing easier. But in Jasmine, that made synchronous testing harder. Every test, whether sync or async, needs the done function to be called. Kind of pointless on a synchronous test. Mocha identifies that if you’ve not called your done function, and there are no pending async tasks, it’s a synchronous test. Out goes the done function, from where it should not have been in the first place.

it('should check promise result', function (done) {
fetchDataFromServer.then(function(result){
expect(result).toEqual(true)
}).catch(function(err){
console.log(err);
}).finally(done);
}

became

it('should check promise result', function () {
return fetchDataFromServer.then(function(result){
expect(result).to.equal(true)
});
}

That’s simply a wow level of simplification.

Then, istanbul. When the tests were initially written, mocha was in it’s infancy, and hence, no support for it in the istanbul coverage reporter. But now, mocha has grown to be an equal to jasmine, and gets the same level of support with istanbul.

Lastly, WebStorm. It is my IDE of choice for development in node.js. And when you want to run Jasmine tests through WebStorm, you’re supposed to follow WebStorm’s simple 13-step process to get it running. And, Jasmine still isn’t a first class citizen. It can run only through the karma test driver. All these steps in, and I was finally able to run the suite from WebStorm. I had to follow more steps to be able to use WebStorm’s debugger, so there’s that.

With Mocha, none of the above. Right click the spec folder, run with Mocha. That’s it. To quote Steve Jobs, it just works. All of it, out of the box.

That basically sealed the deal for me. First class integration into WebStorm, simpler spies, mocks and stubs. All at the cost of what? Adding chai and sinon libraries (2 lines of code), and changing the syntax of the .toEqual calls. So, minimal code change. That’s one move that I’m willing to make.