Improved RequireJS, Jasmine, and Grunt Integration

By Zach Dennis on 28 10 2013

On a recent project PhoneGap project we (Ross, EJ, and myself) wanted to incorporate automated testing into the project. We've always been fond of Jasmine for writing tests in JavaScript, but our efforts were thwarted by the use of RequireJS. We overcame suggested practices by improving the tooling around RequireJS and Jasmine and contributing back to the community at large.

For those whose may not know RequireJS is a JavaScript file and module loader. You define modules with dependencies explicitly stated and RequireJS handles the loading and injecting of them into your modules at runtime. It's a JavaScript-based dependency injection framework with a few more bells and whistles.

RequireJS promotes good things for JavaScript-based projects, but it can be a bit unwieldy at first. For example, you may use libraries or pull in code snippets that are not in the preferred format of RequireJS.

When that happens you need to update your RequireJS configuration to wrap the code in a shim or you need to update the code. In our experience, you typically update code you own, but shim code you don't. This can lead to some decent-sized RequireJS configurations. And it's the configuration that is central to the aforementioned thwarted nature of this post.

More work than necessary

Much to our dismay getting the first Jasmine spec was a whole lot more work than we expected. We either needed to embrace RequireJS in our tests or be good with the decision to avoid it entirely. It seemed appropriate to embrace RequireJS.

We looked into incorporating RequireJS with Jasmine and found that the suggestions for integrating the two left us a bit wanting. Here are the three top suggestions we encountered:

  1. Copy your RequireJS configuration into your spec/ directory and change what you need for your test environment
  2. Copy your RequireJS configuration into your Gruntfile and change what you need for your test environment
  3. Update your RequireJS configuration to include a lot of conditional statements to do one thing if you were running in a test environment and another thing for running the app for real

None of these solutions are very good. The reason they aren't good is that they require you to expend a lot of effort managing unnecessary duplication and/or configuration complexity over time. We wanted to use the majority of the RequireJS configuration from within our app and only change the parts that needed to be changed.

grunt-template-jasmine-requirejs

During our earlier search for incorporating Jasmine and RequireJS we had found the grunt-template-jasmine-requirejs project. It supported the three solutions mentioned a few paragraphs above, but that was it.

We asked ourselves the question "What if we could merge full or partial RequireJS configurations together?"

After some investigation into how Grunt and grunt-template-jasmine-requirejs worked we decided to fork and patch the project, get our changes working, and then submit a pull request back to the project.

Here's how our changes work. First, the traditional grunt-template-jasmine-requirejs usage:

In the above jasmine grunt task requireConfigFile points at src/main.js. When you run grunt jasmine it will generate a SpecRunner.html that pulls in the configuration from src/main.js.

Now, let's say you wanted to override the foo shim for your test. Maybe you want to provide a fake "Foo" module throughout your tests instead of using the real thing. Maybe it's an interface to a third party library that you don't actually want to use in your tests.

Here's the updated grunt task showing how you can do that with an inline requireConfig directive:

This specifies both requireConfigFile and requireConfig. This works by loading requireConfigFile first and then merging any requireConfig directive onto it.

This also works in the case where you don't want to inline your RequireJS configuration in your grunt task:

In this third and final example you see that requireConfigFile specifies an array of files. It will load the left-most file first, src/main.js, and then merge onto it any files that come after that in left to right order (spec/test.js).

We did submit a pull request and it indeed was incorporated back into grunt-template-jasmine-requirejs. You can use this functionality in versions 0.1.3 and above.

If you're a RequireJS and Jasmine user you should definitely check this out grunt-template-jasmine-requirejs and say goodbye to copy and pasting your RequireJS configurations.