Category Archives: Uncategorized

Testing Javascript Singletons

Testing javascript singleton modules can be a challenge, especially ones that maintain some sort of internal state. Examples of singletons that manage internal state are usually either a factory or cache manager. In order to effectively test these sorts of modules, you often either resort to exposing a method to reset the state, or carefully order your tests… until now!

To eliminate this problem, I propose the following solution (assuming you’re using a spec-style test):

  1. Wait to require the module you’re testing until all stubs, spies, and mocks are defined in a beforeEach
  2. require the module you’re testing after stubbing/spying/mocking in the beforeEach
  3. in the afterEach, remove the module you’re testing from the require.cache
    • this effectively destroys the singleton and when it is require‘d again the the beforeEach it will be rebuilt.

Here’s how you remove a module from the require.cache:


I’m assuming that you have your tests in a sibling directory to a lib directory, where your production modules live. If not, you can adjust the path spec accordingly.

Other things to be mindful of are:

  • Event listeners (both DOM and custom events)
  • The state of the DOM itself
  • Callbacks you’ve stored off in the body of stubs and/or spies within your test suite

These are all things that need to be cleared out in the afterEach as well.

But wait, I’m a front-end developer and I use karma or testem to execute my tests in a real browser. Well, I personally believe runners are a source of pain and frustration and should be avoided. However, it is theoretically possible with karma to achieve this by removing the script element that corresponds to the module you’re testing in the afterEach and then append it back immediately after removing it. I am not familiar enough with testem to give equivalent advice.

Setting up a project using karma with mocha and chai

Recently, I was on a quest to evaluate the mocha javascript testing framework
and its direct support for async testing in the browser. I had a hard time finding a complete description of everything that needed to be done in order to get things working with karma for testing in the browser as opposed to testing a node module.

Consequently, I decided it would be useful to create a guide for those who have similar interests to save everyone time. If you want to skip the steps and just download the code, feel free to clone my github repository. As a quick aside, I decided to try mocha thanks to some frustrations with jasmine. Async testing with jasmine can be done, but it does not have first-class support in my opinion. I intend to give buster a spin in the near future as well.

This tutorial assumes that you are on a unix-based system (I used OSX to write the article) and already have a working nodejs and npm installation, therefore installing and configuring a node.js/npm environment is outside the scope of this article. With that said, let’s dig in!

  1. Create a directory to hold the project and navigate to that directory:
    • mkdir <new_dir>
    • cd <new_dir>
  2. We’ll be installing a bunch of npm modules, so let’s run npm init to build our package.json file for us
  3. Let’s install karma with npm. I recommend installing it locally, since it’s likely that you’ll be either executing karma from an IDE (such as webstorm) or as a grunt task. Additionally, one project may be configured for one version of karma while a different project can use a different version if needed.
    • npm install karma --save-dev
    • At the time I wrote this article, the latest stable version of karma was 0.10.8.
  4. Now we’re ready to install mocha:
    • npm install mocha --save-dev
  5. We’ll also need the karma-mocha adapter so we can use mocha as a test framework with karma:
    • npm install karma-mocha --save-dev
  6. An assertion library will need to included as well. I prefer the chai assertion library, since it offers both bdd style assertions using expect and should as well as junit-style assertions.
    • npm install karma-chai --save-dev.
    • Installing chai this way will make it available as a framework within karma, which makes setup much simpler.
  7. I also find sinon to be extremely useful for stubbing and spying, so let’s install it to round out our set of packages required to get our test environment provisioned:
    • npm install karma-sinon --save-dev

At this point, our package.json should have a devDependencies directive that looks something like this:

devDependencies:

We can now create our karma configuration and write some tests. Let’s first make sure that our karma installation is working correctly:

  • ./node_modules/karma/bin/karma --version

That should print out the version number to the console if everything is working correctly. If you want to, you can initialize a karma configuration file using:

  • ./node_modules/karma/bin/karma init

I’ll provide the boilerplate so you can avoid that step. Include the following configuration in a file named

karma.conf.js:

Once that file is ready, we can create a failing test to ensure our configuration is working as expected. Create a new file in src/test called test.spec.js. Create the directory:

  • mkdir -p src/test

And a test file with the following contents:

describe("A test suite", function() {
   beforeEach(function() { });
   afterEach(function() { });
   it('should fail', function() { expect(true).to.be.false; });
});

With our test spec in place, we can run karma and make sure that we can successfully execute failing tests. ./node_modules/karma/bin/karma start karma.conf.js

After running our tests, we should see some output along the lines of

A test suite should fail FAILED

If so, then you’ve successfully configured karma with mocha, chai and sinon. Cheers!

Update: 07/14/14

I recently updated this to use the new 0.12.x version of karma and also added an example Gruntfile.js