XING Devblog

Test your code! Unit testing in JavaScript

| Posted by

Ensuring code functionality in large web applications is quite a difficult task. Nowadays, lots of business logic is handled within the client and you have to make sure that everything works as intended. Maintenance can be very complex if you consider the fact that we currently have about 176,207 lines of JavaScript code and 20 frontend engineers working in different teams.

Imagine, for example, that we have seven sub-modules based on one main module. If you know change something in the main module, the changes could also affect the functionality of each sub-module and, in a worst-case scenario, break every module.

That’s why we write unit tests in our development process for each module to cover and test as much logic as possible. To achieve this we use QUnit as a testing framework. Besides using real assets for Ajax requests, we use Mockjax to mock requests and jQuery as a JavaScript Library

Simple code example

Let’s imagine that we would like to test a module. The module itself does nothing more than toggle (show/hide) an element while also sending out an Ajax request when checking a checkbox.

myModule.html

myModule.js

var myModule = function(container) {
  var container = $(container),
      button    = container.find('[data-toggle="button"]'),
      toggleBox = container.find('[data-toggle="container"]'),
      checkbox  = container.find('[data-checkbox="xhr"]'),
 
      init = function() {
        button.click(function() {
          toggleBox.toggle();
        });
 
        checkbox.change(function() {
          $.ajax("/foo/bar"); 
        });
      };
 
  init();
 
  return { init: init };
};

myModule_test.js

// Package separate tests into a module.
module("myModule", {
  // Will be run before each test.
  setup: function () {
    // Get elements.
    this.container = $("#container");
    this.button    = this.container.find('[data-toggle="button"]');
    this.toggleBox = this.container.find('[data-toggle="container"]');
    this.checkbox  = this.container.find('[data-checkbox="xhr"]');
 
    // Mockjax general setup for each test.
    $.mockjaxClear();
    $.mockjaxSettings.responseTime = 1;
  },
 
  // Will be run after each test.
  teardown: function() {
    // Reset default values for future Ajax requests.
    $.ajaxSetup({ complete: $.noop });
  }
});
 
// Test to run. Tests are queued and run one after the other.
test("check if toggleBox is toggled, when button is clicked", function() {
 
  // Specify number of assertions for this test.
  expect(3);
 
  // Initialize module
  new myModule(this.container);
 
  // Check original state of toggleBox (boolean assertion).
  ok(this.toggleBox.is(":visible"), "toggleBox is initially visible");
 
  // Trigger click event on native button element.
  QUnit.triggerEvent(this.button[0], "click");
 
  // Check state of toggleBox after clicking the button.
  ok(!this.toggleBox.is(":visible"), "toggleBox is hidden after button click");
 
  QUnit.triggerEvent(this.button[0], "click");
  ok(this.toggleBox.is(":visible"), "toggleBox is visible again after button click");
});
 
test("check if ajax request is done", function() {
  expect(1);
 
  // Wait for async request to run.
  stop();
 
  // Mock ajax request with mockjax.
  $.mockjax({ url: "/foo/bar" });
  new myModule(this.container);
 
  // Set default values for future Ajax requests.
  $.ajaxSetup({
    complete: function() {
      ok(true, "triggered ajax request");
      start();
    } 
  });
 
  QUnit.triggerEvent(this.checkbox[0], "change");
});
QUnit Testresult

QUnit Testresult

Conclusion

This example served to test the module’s logic. These tests can now be automated to be run repeatedly as regression tests so we can make sure that future changes don’t affect the basic functionality of this module.

We’ve also done this with over 2,700 unit tests which cover the client‐side logic across XING.com. These tests are integrated into Jenkins and run repeatedly. One of the important facts besides the sheer number of tests is test coverage. Covering 100% of a module is the key, because 100% coverage theoretically means zero false code and is hard to mess up. One can even use techniques like test‐driven development (TDD), which is an iterative process. First of all, a test case is written that will fail. Secondly, the developer produces code to pass that test. Using TDD highly depends on the situation because switching between writing a test and writing code does not always make sense.

As you now see, unit testing is a very important aspect in making sure that your software works. Nevertheless, you can’t completely rely on unit tests. They are a good indicator, but can’t replace real-life testing or cover every edge case that may occur.

About the author

Sascha CacqueuxSascha Cacqueux works as a Frontend Engineer in the Growth Team at XING. He's keen on writing Javascript and unit-tests besides of semantic markup.

XING Profile »


One thought on “Test your code! Unit testing in JavaScript

Leave a Reply

Your email address will not be published. Please fill out the required fields.

  • You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>