Unit Testing

To follow this tutorial you'll need to have installed the SDK, learned the basics of cfx, and followed the tutorial on writing reusable modules.

This tutorial uses the action button API, which is only available from Firefox 29 onwards.

The SDK provides a framework to help creates and run unit tests for your code. To demonstrate how it works we'll write some unit tests for a simple Base64 encoding module.

A Simple Base64 Module

In a web page, you can perform Base64 encoding and decoding using the btoa() and atob() functions. Unfortunately these functions are attached to the window object: since this object is not available in your main add-on code, atob() and btoa() aren't available either. Using the low-level window-utils module you can access window, enabling you to call these functions.

However, it's good practice to encapsulate the code that directly accesses window-utils in its own module, and only export the atob() and btoa() functions. So we'll create a Base64 module to do exactly that.

To begin with, create a new directory, navigate to it, and run cfx init. Now create a new file in "lib" called "base64.js", and give it the following contents:

var window = require("sdk/window/utils").getMostRecentBrowserWindow();
exports.atob = function(a) {
  return window.atob(a);
exports.btoa = function(b) {
  return window.btoa(b);

This code exports two functions, which just call the corresponding functions on the window object. To show the module in use, edit the "main.js" file as follows:

var base64 = require("./base64");

var button = require("sdk/ui/button/action").ActionButton({
  id: "base64",
  label: "base64",
  icon: "./icon-16.png",
  onClick: function() {
    encoded = base64.btoa("hello");
    decoded = base64.atob(encoded);

To run this example you'll also have to have an icon file named "icon-16.png" saved in your add-ons "data" directory. You could download this icon: .

Now "main.js" imports the base64 module and calls its two exported functions. If we run the add-on and click the button, we should see the following logging output:

info: aGVsbG8=
info: hello

Testing the Base64 Module

Navigate to the add-on's test directory and delete the test-main.js file. In its place create a file called test-base64.js with the following contents:

var base64 = require("./base64");
exports["test atob"] = function(assert) {
      assert.ok(base64.atob("aGVsbG8=") == "hello", "atob works");
exports["test btoa"] = function(assert) {
  assert.ok(base64.btoa("hello") == "aGVsbG8=", "btoa works");
exports["test empty string"] = function(assert) {
  assert.throws(function() {
                "empty string check works");

This file: exports three functions, each of which expects to receive a single argument which is an assert object. assert is supplied by the test/assert module and implements the CommonJS Unit Testing specification.

  • The first two functions call atob() and btoa() and use assert.ok() to check that the output is as expected.

  • The second function tests the module's error-handling code by passing an empty string into atob() and using assert.throws() to check that the expected exception is raised.

At this point your add-on ought to look like this:


Now execute cfx --verbose test from the add-on's root directory. You should see something like this:

Running tests on Firefox 13.0/Gecko 13.0 ({ec8030f7-c20a-464f-9b0e-13a3a9e97384}) under darwin/x86.
info: executing 'test-base64.test atob'
info: pass: atob works
info: executing 'test-base64.test btoa'
info: pass: btoa works
info: executing 'test-base64.test empty string'
info: pass: empty string check works

3 of 3 tests passed.
Total time: 5.172589 seconds
Program terminated successfully.

What happens here is that cfx test:

Note the hyphen after "test" in the module name. cfx test will include a module called "test-myCode.js", but will exclude modules called "test_myCode.js" or "testMyCode.js".

  • looks in the test directory of your package
  • loads any modules whose names start with the word test-
  • calls each exported function whose name starts with "test", passing it an assert object as its only argument.

Obviously, you don't have to pass the --verbose option to cfx if you don't want to; doing so just makes the output easier to read.


File Size Date Attached by
1657 bytes 2014-04-09 19:53:18 wbamberg

Document Tags and Contributors

Contributors to this page: Canuckistani, wbamberg
Last updated by: wbamberg,