An Interest In:
Web News this Week
- March 27, 2024
- March 26, 2024
- March 25, 2024
- March 24, 2024
- March 23, 2024
- March 22, 2024
- March 21, 2024
Better CoffeeScript Testing With Mocha
Recently, I’ve been doing a considerable amount of CoffeeScript work. One problem I ran into early-on was testing: I didn’t want to manually convert my CoffeeScript to JavaScript before I could test it. Instead, I wanted to test from CoffeeScript directly. How’d I end up doing it? Read on to find out!
You’ll need to have Node.js and Node Package Manager installed.
Before we continue on, I’ll point out that you need to have a decent knowledge of CoffeeScript for this tutorial; I won’t be explaining the bits and pieces here. If you’re interested in CoffeeScript, you should check out the CoffeeScript tuts available here on Nettuts+, or the CoffeeScript documentation.
Additionally, you’ll need to have Node.js and the Node Package Manager (npm) installed for this tutorial. If you don’t have ‘em installed, no worries: head over to nodejs.org and download the installer for your platform; then, well, install it!
Meeting Mocha and Chai
We’ll be building the beginnings of a todo list application (cliché, I know). These will be CoffeeScript classes. Then, we’ll write some tests with Mocha and Chai to test that functionality.
Why both Mocha and Chai? Well, Mocha is a testing framework, but it doesn’t include the actual assertions component. That might sound strange: after all, there isn’t much more to a testing library, is there? Well, there is, in Mocha’s case. The features that brought me to the library are two-fold: the ability to run tests from the command line (instead of having an HTML page to run them in the browser), and the ability to run test in CoffeeScripts, without having to convert that code to JavaScript (as least manually: Mocha does it behind the scenes). There are other features, too, that I won’t be talking about here, including:
- You can easily test asynchronous code.
- You can watch for especially slow tests.
- You can output the results in a number of different formats.
And on, and on. See more at the Mocha home page. To install Mocha simply run npm install -g mocha
, and you’re set.
As for Chai: it’s a great assertion library that offers interfaces for doing both BDD and TDD; you can use it both in the browser or on the command line via node, which is how we’ll use it today. Install it for Node, via npm install -g chai
.
Now that we have our libraries installed, let’s start writing some code.
Setting Up Our Project
Let’s begin by setting up a mini project. Create a project folder. Then, create two more folders in that one: src
, and test
. Our CoffeeScript code will go in the src
folder, and our tests will go in, you guessed it, the tests
folder. Mocha looks for a test
folder by default, so by doing this, we’ll save ourselves some typing later.
Mocha looks for a test
folder by default.
We’re going to create two CoffeeScript classes: Task
, which will be a todo item, and TaskList
, which will be a list of todo items (yes, it’s more than an array). We’ll put them both in the src/task.coffee
file. Then, the tests for this will be in test/taskTest.coffee
. Of course, we could split ‘em into their own files, but we’re just not going to do that today.
We have to start by importing the Chai library and enabling the BDD syntax. Here’s how:
chai = require 'chai'chai.should()
By calling the chai.should
method, we’re actually adding a should
property to Object.prototype
. This allows us to write tests that read like this:
task.name.should.equal "some string"
If you prefer the TDD syntax, you can do this:
expect = chai.expect
… which allows you to write tests like this:
expect(task.name).to.equal "some string"
We’ll actually have to use both of these, as you’ll see; however, we’ll use the BDD syntax as much as possible.
Now we’ll need to import our Task
and TaskList
classes:
{TaskList, List} = require '../src/task'
If you aren’t familiar with this syntax, that’s CoffeeScript’s destructured assignment at work, as well as some of its object literal sugar. Basically, our require
call returns an object with two properties, which are our classes. This line pulls them out of that object and gives us two variables named Task
and TaskList
, each of which points to the respective class.
Writing Our First Tests
Great! Now, how about a test? The beauty of the Mocha syntax is that its blocks (describe
and it
) are identical to Jasmine’s (both being very similar to RSpec). Here’s our first test:
describe 'Task instance', ->task1 = task2 = nullit 'should have a name', ->task1 = new Task 'feed the cat'task1.name.should.equal 'feed the cat'
We start with a describe
call: all these tests are for a Test instance. By setting test1 = test2 = null
outside our individual tests, we can use those values for multiple tests.
Then, in our first test, we’re simply creating a task and checking to see that its name property has the correct value. Before writing the code for this, let’s add two more tests:
it 'should be initially incomplete', ->task1.status.should.equal 'incomplete'it 'should be able to be completed', ->task1.complete().should.be.truetask1.status.should.equal 'complete'
Ok, let’s run these tests to make sure they’re failing. To do this, let’s open a command prompt and cd
to your project folder. Then, run this command:
mocha --compilers coffee:coffee-script
Mocha doesn’t check for CoffeeScript by default, so we have to use the --compilers
flag to tell Mocha what compiler to use if it finds a file with the coffee
file extension. You should get errors that look like this:
If, instead of seeing that, you get the error Cannot find module '../src/task'
, it’s because your src/task.coffee
file doesn’t exist yet. Make said file, and you should get said error.
Coding Our First Features
Well, now that we have failing tests, it’s time to write the code, correct? Open that src/task.coffee
file and let’s get cracking.
class Taskconstructor: (@name) ->
Just this is enough to get our first test passing. If you aren’t familiar with that parameter syntax, that just sets whatever value was passed to new Task
to the @name
(or this.name
) property. However, let’s add another line to that constructor:
@status = 'incomplete'
That’s good. Now, head back to the terminal and re-run our tests. You’ll find that—wait a second, nothing’s changed! Why aren’t our first two tests passing?
A simple problem, actually. Because the CoffeeScript compiler wraps the code in each file in a IIFE (or, a self-invoking anonymous function), we need to “export” anything we want to be accessible from other files. In the browser, you’d do something like window.Whatever = Whatever
. For Node, you can use either global
or exports
. We’ll be using exports
, since 1) that’s considered best practice, and 2) that’s what we prepared for when setting up our tests (remember our require
call?). Therefore, at the end of our task.coffee
file, add this:
root = exports ? windowroot.Task = Task
With that in place, you should find that two of our three tests are now passing:
To get the last test to pass, we’ll have to add a complete
method. Try this:
complete: ->@status = 'complete'true
Now, all tests pass: