Cross environment Javascript with Requirejs, Karma and Mocha

Posted by: Seth Lakowske

My goal is to use one language and share logic across two environments, the server and the browser. I want to do this easily.

Update (2015-01-21): I no longer use RequireJS.  I now use and recommend Browserify to share logic across the server and browser.

Why CommonJS didn’t work in the browser
I started using the CommonJS module pattern, because CommonJS is the default on Node and I was new to Javascript. But I soon found CommonJS did not port to the browser easily. When I tried using Browserify, I ran into an exception while browserifying a javascript testing library called Sinon into my javascript bundle. I didn’t try to work around the problem because I knew I would likely want to use another CommonJS library that had never been tested in a browser environment. I wasn’t meeting my goal of easily shared logic since I was missing one big environment, the browser. I read that RequireJS contained the features I needed, but since it wasn’t CommonJS format, I was slow to try it. After running out of patience with Browserify, I relented and gave RequireJS and the AMD module format a try.
RequireJS in the browser
RequireJS has allowed me to share logic across node and the browser. I believe it was written with this idea in mind. If RequireJS can’t find a module dependency in my project code, it will search through Node’s node_module directory for the dependency. This functionality ties in to Node’s large library of NPM modules. I can include NPM modules in my browser bundle too, which increases the browser side library selection. So far, I am happy with RequireJS.
Testing with Mocha
Another vital requirement of an environment is a method of proving the viability of my logic across environments. I tried Nodeunit, but it is designed for node and I need my test runner to work in the browser too. I tried Jasmine, but asynchronous testing is only recently(Sep’ 2013) being added to the framework. I then arrived at Mocha because it runs well across environments, both server and the browser. It has support for asynchronous testing, which I find is a requirement due to the integration/regression tests that I desire. These integration tests are written to import javascript, start servers and query databases. As you can imagine, these are asynchronous operations.

I use Sinon for test spies. These “spies” double as delegate objects and watch calls made to them (or not made to them). I can later inspect the spy object and verify that certain calls were made. I can check the arguments passed and the number of times a method is called.

I use Chai for my assertion library. It allows me to say things like chai.assert.equal(a, "test text string"); These tests are used to verify my expectations about an implementation that I am testing.

Automating with Karma
I want my tests to be run without me putting much effort in. I want to be editing code and when I save, tests get run and my logic is tested. To do that I use Karma in the browser environment and Mocha’s test runner api in the server (Node) environment. I can write a single test and it can be run in both environments. Right away I’ll know if something isn’t working in one environment or the other. It is a proof of the cross platform design of my code and of the particular logic I am testing.
Final Words
So that summarizes my choice of Javascript technologies to accomplish shared logic across two environments. I modularize my code with the cross platform RequireJS library, test with Mocha’s test framework and automate testing on the browser with Karma. It took me some time to arrive at this mix of libraries, but I finally have something that satisfies my need for one language cross environment coding.