Asynchronous JavaScript Testing in Jasmine, Mocha, and Vows

The rise in popularity of JavaScript, especially on the server-side, has introduced more and more developers to asynchronous programming. Asynchronous, event-driven programming also requires a change in testing. In this post, we’ll look at how three popular JavaScript testing frameworks support testing asynchronous code.

Jasmine

The first framework we’ll look at is Jasmine. Jasmine is a popular testing framework built to test client-side JavaScript. jasmine-node is a fork of Jasmine for testing server-side JavaScript in node.js. Both offer support for testing asynchronous code. Let’s first look at Jasmine’s support for asynchronous testing, then we’ll refactor to jasmine-node’s cleaner, simpler syntax.

The following example uses Jasmine’s asynchronous testing support to test the readFile function in the node.js file system module.

In Jasmine, asynchronous code has to be executed in a function passed to the runs function. In our example, this is where we call the asynchronous readFile function. Our readFile callback checks for errors, saves the contents of the file, and then sets a “done” flag.

After passing our asynchronous code to runs, we need to tell Jasmine to wait until our asynchronous code has executed. Jasmine’s waitsFor function expects a function that returns a boolean value indicating the status of your asynchronous code. In our example, we return the “done” flag that we set in our asynchronous callback.

Expectations of asynchronous code must also be declared in a function passed to runs. Above, we expect some file content to have been read by node.js.

jasmine-node

jasmine-node integrates Jasmine with node.js. It adds simpler, more elegant asynchronous testing support to Jasmine. The following is a rewrite in jasmine-node of our previous test.

In jasmine-node, a “done” function is passed to any setup, teardown, or example functions that declare a parameter. jasmine-node will then wait until the passed “done” function is called before executing the rest of the test. If the callback isn’t called after 5 seconds (the default timeout) the test will fail.

Mocha

mocha is another popular JavaScript testing framework. Its asynchronous testing support is identical to jasmine-node‘s.

The following is an example using mocha to test a simple express app (for simplicity, the express app is defined in the same file).

This example is similar to our jasmine-node example. In it, we declare a single parameter anonymous function for our beforeEach hook. In our asynchronous callback we invoke the “done” function that was passed to our beforeEach hook. This tells mocha, which is waiting for the “done” function to be invoked, to continue executing the rest of the test.

Vows

Vows is yet another JavaScript testing framework. Vows claims to have been developed from the start to test asynchronous code.

The following is a vows test for connecting to a socket.io server, registering an asynchronous message listener, and verifying the listener is called.

In vows, a topic is a test fixture. A topic is passed to each vow (a function that runs assertions on a topic). For asychronous tests, vows provides a callback function. This callback function should be passed to your asynchronous code. When this callback is called, it will pass on its arguments to each vow. The above test includes a single vow that expects a welcome message from the socket server, after connecting to it.

One gotcha with vows is that if you use the callback function in your topic function, then your topic function must not return anything. Since CoffeeScript functions implicitly return their last expression, we had to explicitly return undefined. Vows offers an alternative syntax for asynchronous topics using promises. Here’s an example test using this syntax.

In this test, we define a topic that returns a promise. This promise emits a “success” event in the asynchronous Google.search callback. The HTTP response object passed to the callback is passed along in the “success” event, which is in turn, passed along to the single vow.

Get Some Coverage

All of these testing frameworks offer good support for writing clear tests for asynchronous code. Personally, I prefer the asynchronous syntax in jasmine-node and mocha. It’s clean and easy to understand. Vows is impressive, but I’m not a fan of its novel syntax and terminology (I just can’t get used to calling a test a “vow”). As a fan of CoffeeScript, I’m also annoyed by vows’ return value requirement for asynchronous topics. I suggest you give each framework a try. The important thing is to get some coverage for all your convoluted JavaScript code.

About Jared Carroll

After a short stint in the fashion industry Jared found his true calling at Carbon Five. Yes... he looks like a serial killer in this photo. But really he is as gentle as a flower.
This entry was posted in Process, Web and tagged , , , , . Bookmark the permalink.

8 Responses to Asynchronous JavaScript Testing in Jasmine, Mocha, and Vows

  1. Rob Pak says:

    Thanks for the post, Jared. I didn’t realize Jasmine had asynchronous testing support with the ‘runs’ and ‘waitsFor’ functions. Thanks!

  2. Alon Salant says:

    Nice summary, Jared.

    One other nice thing about jasmine-node is that if you are doing client-side testing with Jasmine (I personally like jasmine-headless-webkit) then you are using the same test framework and syntax server and client-side. It also has support for some other jasmine addon features like junit and Teamcity-style reporters for use with continuous integration servers.

    Unfortunately, I think the wrapper code to make jasmine available in node is more complicated than it needs to be and a bit messy. For the average user, I don’t think you’ll notice a difference. I found this out when I added a patch to jasmine-node to support the ‘done’ callback for async specs. I liked that feature in mocha so implemented it for jasmine-node.

    • Jared Carroll says:

      Thanks Alon.

      I’m currently having trouble testing some client-side backbone.js code that’s part of a node.js app. I’m using mocha to test the server-side node.js code but can’t get jasmine-node and jasmine-headless-webkit working together with snockets ( https://github.com/TrevorBurnham/snockets ) and the client-side JS.

      Do you have a sample repo with jasmine-node setup for both client and server-side testing?

      • Alon Salant says:

        Didn’t get notified that you responded ;-( Glad I checked back.

        We’re not using snockets so haven’t had to solve exactly that problem. We’ve been using connect-assets for similar functionality but it doesn’t come in to play in testing.

        When you say jasmine-node and jasmine-headless-webkit working together, I’m not actually sure what you mean. We use them independently – jasmine-node to unit test server side code and to test express ‘controller’ code including the rendered views. (Cool to be able to use jQuery to do view assertions!)

        And then for client-side, we use JHW to test Backbone models, views and other client-side functionality. We’re using browserify to package and deliver client-side javascript so had to build some infrastructure to get that working with JHW.

  3. Travis Herrick says:

    Just curious… Why did you use coffescript for all of the code samples?

    • Jared Carroll says:

      Haha, seriously, a blog post about JavaScript testing with no JavaScript. CoffeeScript is the hottness; I can’t be wasting my time typing semi-colons, parens, and curly braces.

      • demisx says:

        +1 for using CoffeeScript! So much more clarity.

      • Harold says:

        Coffee script adds an unnecessary layer of complexity (in your examples) to readability for native Javascript learners. I can see the advantages of using it to maintain your own code, but if you’re doing a teach-out, it’s just frustrating.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>