I’ve had a number of people ask about testing browserify modules, after I posted about how I’m using browserify with Backbone and Marionette. The truth was, I had not yet looked in to this and I wanted to remedy that.
But, when i first started thinking about this, I wasn’t quite sure what to do. It seemed there were some pretty big hurdles in front of me.
Here’s the problem: Browserify modules don’t export anything to the global namespace unless you tell them to. I don’t want to do that. That would defeat the purpose of having the code wrapped in a module. I also don’t want to load the entire application code base in to my unit tests. That wouldn’t be a unit test… or at the very least, it would be a very slow test that requires a lot of hacks and workarounds to isolate the code that I need. Sometimes, though, the code I need still wouldn’t be available because it doesn’t exposed except through the Browserify module.
So I said to myself, “Self! You need to get the code out of the Browserify module, in to the test!”
And then I remembered something: I test NodeJS modules by doing a require on the module that I want to test. So why not do the same thing for the browser based testing, with Browserify modules? Browserify modules are basically CommonJS modules… they export things which can be imported through a require statement.
Yup. Let’s do that.
Require The Module I Need
This turned out to be pretty easy. When I need code from my “Patient” object/module, I require that module in my Jasmine spec file.
Bam. Patient object available in my spec file. Now I just need to run a Browserify build on my specs files, and load the result in to my Jasmine test runner.
Testing In A (Headless) Browser
There’s a thousand ways to do that, and I’m using grunt-contrib-jasmine – but you could easily just hand code a spec runner file and use the standalone Jasmine, or anything else you want. To do this with grunt and automate the process, I need grunt-browserify and grunt-contrib-jasmine configured.
In this setup, I am having browserify look at the “specs” folder, bundling all of them together. Since my spec files each require the needed module to test, the bundle created will have the right code available in the right place. Now I need to point Jasmine as the browserify output.
In this setup, I’m pointing Jasmine at the “infrastructure.js” file that I mentioned in my Browserify + Backbone post. This is the file that contains backbone, marionette and my other core infrastructure (the things that don’t change very often). Then, instead of pointing Jasmine as the specs source files, I’m pointing it at the browserify bundled specs file. This one bundle file contains all of the specs that came from the specs folder, plus all of the code that was required in to those specs.
The result is a set of specs that run as expected, using PhantomJS (as built in to grunt-contrib-jasmine)!
Is There A Better Way?
This isn’t perfect, by any means. Look at the require statement in the specs file, for example. I had to use a pretty ugly path to get to the file that I wanted. Now, I could put my spec files next to my source files and that would solve the require problem. But I prefer to have my spec files organized in a specs folder.
I’m also waiting a few seconds per test run, at this point. It takes a moment to re-bundle all of the files with Browserify and then run the specs. Personally, I’m ok with this right now. But I can see that this may be a problem for people that demand sub-second specs – especially when the build becomes huge and requires a lot of time to bundle. Cutting down the size of the bundle by using an infrastructure.js file separate from the build is actually proving to be a good thing in this case.
Knowing there are some potential problems here, I’m interested in seeing how other people are unit testing Browserify modules. If you know of some good examples, or are doing this in your own projects, drop a comment and let me know. I’d love to see what solutions other people have come up with.