Testing Browserify Modules In A (Headless) Browser

NewImage

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.

Whack-A-Mole Modules

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)!

NewImage

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.


Get The Best JavaScript Secrets!

Get the best kept secrets of JavaScript, and the most important career advice you'll ever hear!
Don't miss out: join my list to get all the inside info!

  • http://loose-bits.com/ Ryan Roemer

    I’ve got a reference Backbone.js project for teaching Browserify, Mocha, Jasmine, etc. (https://github.com/FormidableLabs/notes/tree/master/full/browserify) and I also bundle to be able to manually run the tests in a Browser (https://github.com/FormidableLabs/notes/blob/master/full/browserify/Gruntfile.js#L49-L69).nnI also use the same bundle for Karma to run browsers headless for Mocha and Jasmine (https://github.com/FormidableLabs/notes/blob/master/full/browserify/Gruntfile.js#L87-L120)nnnnI have a full implementation of the exact same app and tests in AMD/RequireJS, and things don’t look that different — just switching AMD to CJS format. At least for this small project, there’s no real difference in appearance or functionality of the app or test code across both stacks…

    • http://mutedsolutions.com Derick Bailey

      thanks for the links! will check those out.nnnregarding amd’s “win” of not bundling – yeah, for time to get the tests started i can see that may be an advantage. but generally speaking, i don’t see this as a big win anymore. source maps make my life easy w/ the bundles. :)

  • http://maxantoni.de/ Maximilian Antoni

    I wrote a tool for this exact use case: https://www.npmjs.org/package/mochifynnIt browserifies your Mocha tests and runs them in PhantomJS or a Selenium WebDriver service (e.g. SauceLabs). It also comes with coverage support. Have a look.

  • sharpoverride

    In your article you write that “Iu2019m 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.”nnnnYou should use the watch:true, keepAlive: true flags in your browserify configuration so that it runs watchify in the background. Thus it will only update what you change. And it’s so fast that you hardly ever notice it.nnnNext step should be to configure jasmine to re-run the tests when the generated browserify test file changes. That is what we’ve done with karma.nnnI’m glad you are getting more attention to browserify, it’s a great tool and it makes it easy to work in the same way you would in node.js.nnnI’d love to see you use multiple modules and have them imported in your unit tests, we haven’t gotten arround to doing that.

  • i_like_robots

    I got around the inefficiencies of building and re-building multiple packages for testing by injecting getters and setters into each module using Rewireify. I covered the setup in an article here: http://maketea.co.uk/2014/05/22/building-robust-web-apps-with-react-part-3.htmlnnAlthough the article is primarily about testing React I cover the process of testing Browserify/bundled CommonJS packages.