Loading...
Loading...
Marco Franssen

In this blog post, I want to highlight the similarities and differences between Jasmine and Mocha. I'll focus on the latest versions at the time of writing: Jasmine 2.4 and Mocha 2.3.
When comparing the libraries at NPMCompare, you'll notice that Mocha is the more popular framework at the time of writing. It has more frequent updates, more downloads and more forks on GitHub. Both frameworks are pretty popular, though, and have a lot in common. Let's take a closer look.
Both libraries have a similar API for structuring your tests. They let you group tests in a describe block, and both support asynchronous testing with a callback method (done). The next example shows the structure shared by both frameworks.
describe("User", function () {
describe("When getting the users details", function () {
it("should contain the full name", function (done) {
// assertions go here
});
});
});Please note that this shared asynchronous behavior only applies from Jasmine 2.x onward. In Jasmine 1.x, you had to use methods such as waitsFor to write asynchronous tests.
Both libraries are also supported by the popular test runner Karma. Karma can run your tests in different browsers, such as Chrome, Firefox and PhantomJS. It watches your tests and source files for changes, then automatically runs the tests in the browsers you've configured. This gives you continuous feedback while developing your code.
The differences start with assertions. Mocha doesn't come with a built-in assertion library, but several are available:
Many developers seem to choose Chai as their assertion library for Mocha. While you can use Jasmine as is to write tests, you'll need to load a separate assertion library with Mocha. Let's take a closer look at Chai, as I have some hands-on experience with it. Chai comes with three assertion styles: should, expect and assert. Its expect style is similar to Jasmine's, making it the easiest option when migrating between the two frameworks.
expect(calculator.multiply(3, 4)).toEqual(12);expect(calculator.multiply(3, 4)).to.equal(12);Mocking in JavaScript comes in the form of spies. A spy replaces a function in your code so you can control its behavior in a test. For example, you can record how that function is used and make assertions about its usage. Some concrete examples are:
Mocha doesn't come with built-in mocking, either. To use mocks or spies with Mocha, you'll need another library, such as Sinon. Sinon is a powerful library that offers functionality equivalent to Jasmine spies, along with some additional features. In Jasmine, you can create a spy like this:
// existing methods
var blogPostSaveSpy = spyOn(BlogPost.prototype, "save");
// non existing method spy
var spy = jasmine.createSpy();Sinon offers three approaches: spies, stubs and mocks. Each has some subtle differences. For example, a Jasmine spy won't call through to the method being spied on by default, while a Sinon spy will. Here's a small example:
spyOn(blogpost, "publish").andCallTrough();sinon.spy(blogpost, "publish");A Sinon stub behaves like a Jasmine spy by default. For example:
spyOn(blogpost, "publish").andReturns(200);sinon.stub(blogpost, "publish").returns(200);In many situations, Jasmine spies will cover everything you need for mocking. If you need a few more features, though, you can also use Sinon with Jasmine.
Asynchronous tests are useful for code that makes Ajax calls or, in Node.js, accesses a database or the file system. In those cases, you need to tell Jasmine or Mocha when your code has finished executing. Callbacks and promises are two ways to handle asynchronous code. The following examples show how to test both approaches. Imagine that the Blogpost.getById method returns a promise that resolves with a user object. The promise example uses Bluebird for conveniences such as the catch and finally methods.
describe("When loading a blog post from database", function () {
it("should return an instance of Blogpost", function (done) {
BlogPost.getById(1)
.then(function (post) {
expect(post instanceof Blogpost).toBe(true);
})
.finally(done);
});
it("should return the user with id 1", function (done) {
BlogPost.getById(1)
.then(function (post) {
expect(post.id).toEqual(1);
})
.finally(done);
});
it("should fail with a 401 for non-existing posts", function (done) {
BlogPost.getById(2)
.catch(function (err) {
expect(err.status).toEqual(401);
})
.finally(done);
});
});Notice that we use finally to call the done function. This ensures that done is invoked even if the test fails or execution doesn't enter then or catch.
describe("When loading a blog post from database", function () {
it("should return an instance of Blogpost", function (done) {
BlogPost.getById(1, function (err, post) {
expect(post instanceof Blogpost).toBe(true);
done();
});
});
it("should return the user with id 1", function (done) {
BlogPost.getById(1, function (err, post) {
expect(post.id).toEqual(1);
done();
});
});
it("should fail with a 401 for non-existing posts", function (done) {
BlogPost.getById(2).catch(function (err, post) {
expect(err.status).toEqual(401);
done();
});
});
});Note that we invoke done after making our assertion to tell Jasmine/Mocha that the asynchronous code has finished. We're using Jasmine in these examples, as you can tell from the assertions. You could use the same approach with Mocha, since both frameworks handle callback-based asynchronous tests in the same way.
Overall, I think Mocha gives you a little more flexibility, since you can pick your own assertion and mocking libraries. That comes at the price of a slightly more complex setup and some additional dependencies. Which library to use depends on your project and personal preferences. Choosing Jasmine doesn't prevent you from using Sinon; you can still benefit from its fake server, for example. Both frameworks can do the job. As of Jasmine 2.x, I think Jasmine is back in the race with Mocha.
Also consider reading one of my previous blog posts:
That post has more detailed examples of using Mocha with Chai assertions and Sinon mocks. Thanks for reading! I hope this comparison was useful. Please leave a comment if you have questions or remarks.
Marco Franssen
Use the original Let's Encrypt client in a Vagrant VM to request an Azure website certificate through a reverse proxy, then export and upload a PFX.
Marco Franssen
Set up separate Windows 10 boot profiles with bcdedit to switch between Hyper-V for the Windows Phone emulator and VirtualBox for Linux development.
Marco Franssen
Configure SSH keys, Pageant, agent forwarding, and Git Bash on Windows to work with Linux VMs and Git without repeatedly entering key passphrases.
Marco Franssen
Use Gulp.js to automate PHP linting, PHPUnit tests, coding standards, documentation, and static analysis, with watch tasks for immediate feedback.