Category Archives: Javascript

Just Wrote My First Javascript Unit Tests

I have written a lot of unit tests in C# and finally even came to terms with test-driven development (which I think is awesome – after being afraid of it for such a long time, I admit). It was never my task to write much Javascript code and I only tried bits and pieces here and there. So, naturally, I only cared about getting the job done (and get back to my shiny C# world) and not think too much about readablity, maintainability and code coverage. Only recently Javascript became a bigger part of my developer life (mostly client-side, browser-interpreted). I never liked (reading) the jQuery soup and also had the advantage of having access to frameworks like AngularJS or Backbone.js, so luckily I was in a very fortunate position to not just write the code but also divide it into modules and, greatest of all, easily test it.

After I wrote a couple of smaller and bigger Angular apps, this post finally pushed me in the right direction. The approach described in it uses Jasmine and the Angular Mock Module.

Let’s say we have this silly little service:

my.namespace.services = angular.module('my.namespace.services', []);

my.namespace.services.MenuService = function () {};
my.namespace.services.MenuService.prototype = {

    entries: [],

    updateEntries: function (entries, selected) {
        // ...
    },

    chooseEntry: function (entry) {
        // ...
    }
};

my.namespace.services.factory('$menuService', function() {
    return new my.namespace.services.MenuService();
});

It helps me creating and updating a dynamic menu as well as communicating menu selections between different apps (for instance, the actual menu app and whatever app is the main app). To test this, I write something like this:

describe('MenuService Tests', function () {
    var menuService;

    beforeEach(function () {
        module('my.namespace.services');
        inject(function ($menuService) {
            menuService = $menuService;
        });
    });

    it('should initially have no entries', function () {
        expect(menuService.entries.length).toBe(0);
    });

    it('should have some entries after adding some', function () {
        var newEntries = [{ title: 'some title' }, { title: 'some other title'}];
        menuService.updateEntries(newEntries);
        expect(menuService.entries).toBe(newEntries);
    });
});

The describe function call sets up the whole suite of tests, here it is called MenuService Tests. The beforeEach function call sets up the menu service using Angular Mocks. The it function calls are the actual tests, nicely named using the actual function name and the first string argument, creating a more human-readable touch.

The first test just checks for the entries array being empty at the beginning, again using the very human-readable assert call expect(something).toBe(somethingIExpect). The second test checks for entry updates to the service being actually available on the service.

You run the tests via an HTML page like this (just open it in a browser and the tests will be run for you via Jasmine and the results will be nicely presented to you – like at the bottom of the Jasmine website):

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8" />
    <title>MenuService Tests</title>
    
	<!-- Jasmine -->
	<link rel="stylesheet" href="path/to/jasmine.css" />
        <script src="path/to/jasmine.js"></script>
        <script src="path/to/jasmine-html.js"></script>
    
	<!-- Angular -->
	<script src="path/to/angular.min.js"></script>
        <script src="path/to/angular-mocks.js"></script>
    
	<!-- Code under test -->
	<script src="path/to/menuService.js"></script>
    
	<!-- Test code -->
	<script src="path/to/menuServiceSpec.js"></script>
    
	<!-- bootstraps Jasmine -->
	<script src="path/to/jasmineBootstrap.js"></script>
  </head>

  <body>
    <div id="HTMLReporter" class="jasmine_reporter"></div>
  </body>

</html>

The Jasmine bootstrap code looks like this:

(function () {
    var jasmineEnv = jasmine.getEnv();
    jasmineEnv.updateInterval = 250;
    var htmlReporter = new jasmine.HtmlReporter();
    jasmineEnv.addReporter(htmlReporter);
    jasmineEnv.specFilter = function (spec) {
        return htmlReporter.specFilter(spec);
    };

    var currentWindowOnload = window.onload;
    window.onload = function () {
        if (currentWindowOnload) {
            currentWindowOnload();
        }
        execJasmine();
    };

    function execJasmine() {
        jasmineEnv.execute();
    }
})();

Voila!