I have a number of rules of thumb that I use when doing TDD. One of them is to test only the essential aspects of the code I’m writing. I want to make sure that the key parts of my code work the way I want without over-constraining the rest. That way, my tests stay robust and don’t break as often when unrelated code changes.

Lately, I’ve been using Jasmine to test my JavaScript and CoffeeScript code and I recently discovered some handy tools in provides.

Let’s say I’m testing a table cell that responds to a click by opening up an edit box on the entry represented by the cell. The table might be in a disabled mode, in which case the edit box should open in read-only mode. There might be other flags that get passed in along with the read-only flag.

I might write a test like the following:

Verbose Test
it "opens the edit box in read-only mode", ->
spyOn(cell, "openEditBox")
view.$('.edit-icon').click()
expect(cell.openEditBox).toHaveBeenCalled()
args = cell.openEditBox.mostRecentCall.args
expect(args[0] instanceof Entry).toBeTruthy()
expect(args[1].readOnly).toBe(true)

Obviously, I’ve left out a lot of surrounding detail here.

This is a verbose test, but it constrains the code only as much as necessary:

  • I ensure that openEditBox is called.
  • I ensure that the first argument is some kind of Entry, but I don’t care which one. In real code I probably do care, but for this example I don’t.
  • I ensure that the second argument has a property named readOnly that has the value true. I don’t care what else it has; this test isn’t concerned with that.

Jasmine provides some handy matchers that allow the above test to be written much more succinctly:

Cleaner Test
it "opens the edit box in read-only mode", ->
spyOn(cell, "openEditBox")
view.$('.edit-icon').click()
expect(cell.openEditBox).toHaveBeenCalledWith(jasmine.any(Entry),
jasmine.objectContaining(readOnly:true))

These matchers do the same job that I did explicitly in the more verbose version of the test. They can also be used to match return values in expect calls.