JavaScript Testing with Jasmine and RequireJS

Jasmine is a great JavaScript testing framework, but I grew tired of managing the jasmine gem to work with my JavaScript code in a Rails application. For each new file, you have to add it to your jasmine.yml (or use a wildcard include). This means that every file is loaded every time you run your tests, even if you are only running a small batch of them. I wanted to address that.

EDIT: Note that I no longer advocate the use of RequireJS. If you still find this useful, that’s awesome.

Problems with Jasmine

In a parallel endeavor, I grew tired of namespacing my JavaScript code. So, I brought in RequireJS and setup all of my modules to use the AMD format it requires.

However, testing became a problem. I needed a way for Jasmine to play well with RequireJS. I could just require the modules I want inside a test, then waitFor it to be loaded, but that felt rather messy. So, I decided to patch Jasmine’s it and describe methods to do what I wanted.

Adding RequireJS Support to Jasmine

RequireJS Config

When the Jasmine server runs, it exposes a path to your app’s /public folder under /public. The problem is that you would normally reference those files from your website root. So, your bootstrap file needs to know if it is being used in a testing environment. You can test for that and act accordingly.

jasmine.yml

The jasmine.yml file doesn’t need to include any specific (or wildcarded) spec files.

Patching it and describe

This step involved a lot of work. Essentially, we override the global it and describe methods to support the following.

  • One argument => pending
  • Two arguments => normal behavior
  • Three arguments => RequireJS behavior

The standard method for using RequireJS to import a module is to call define ['module1', 'module2'], (module1, module2) ->. So, I decided to follow the same signature in the it and describe calls, making this valid.

When using the Jasmine gem, the Jasmine test runner page is setup to run the tests in a window.onload event handler. The problem here is that we want to wait for our modules to be loaded before registering our specs. The new spec methods will use RequireJS to load the necessary modules asyncronously. If we leave the window.onload handler there, it will run before our modules are loaded and our specs will never be registered.

Thus, we need to wait for our specs to be registered before running the test suite. I handled this with a simple load counter, but there’s probably a race condition with nested module requirements in specs. For now, this works pretty well.

Testing CSS

The traditional use of the jasmine.yml configuration file has you list all of your CSS files. It seems that people usually do this so that they can test the visibility display: none or display: block of an element. I feel that this is far from necessary. So, I created a little tool (called Hidey, requires cssom) to extract out your display: none declarations from your css files into a new smaller file. Now, you only need to include that one file to be able to test your visibilities.

Since writing that tool, however, I’ve changed my view on testing visibility. I’d rather let my CSS handle that and simply test for the existence of a class that should imply visibility. The simplest example would be to test an element for a hide class if you want it to be hidden.

Putting It All Together

Now, I can write specs that look like this!

Giving Back

I considered submitting a pull request to Jasmine to add RequireJS support, but I’m not completely happy with how it works right now. The more I manipulate Jasmine to do what I want, the more I realize I should write my own testing framework (again, although attempt #1 was many years ago and pretty awful). But, I’ll save that for another day.