Pages

Saturday, February 16, 2013

JavaScript Unit Testing in Visual Studio Part III

Testing Require.JS Modules

require js
This is the 3rd post in this series, talking about JavaScript Unit Testing in Visual Studio.

In the first post we've set up the expectations, and one of them was compatibility with Require.JS, and in the previous post, we've went all the way from downloading the libraries, to running headless tests against multiple browsers from Visual Studio.

In this post we'll take it another step toward a modular application, and check out how this can also get tested with the stack we've already discussed.


Tackling Complexity With Require.JS


So, if you're not already using Require.JS and asking yourself how could you go without it, let's see what problems it solves and why you should use it.

Simply put, Require.JS is a JavaScript file and Module Loader, at least this is what stated on it's homepage.

Since JavaScript doesn't provide, yet, a mechanism to define modules, developers usually go with the Module Pattern, or do object nesting to get something that can remind a namespace in other language, like C# for example.

Both techniques are good in the sense that both provide some kind of encapsulation and scope, and keep the global namespace (the Window object) clean.

So what are the benefits you get by moving to Require.JS? Some of them might be:

  • First and most important, you get decoupling between modules.
  • All the headache of finding the exact order to load script because of dependencies is gone, as it will resolve dependencies automatically. 
  • If you're building SPAs (Single Page Applications), you can utilize text plugin to load markup fragments on-demand, compile them and render them on the client. Works great with Underscore and BackBone.JS.

I hope it's enough to get you motivated, so let's see how to use it. I'll continue from where we stopped in the previous post, assuming you followed and got the project running...

Download the Libraries

  • Go and download Require.JS.
    Although not directly relevant for the purpose of this post, I would really recommend also getting the other plug-ins that come along with it.
  • Add Require.JS to the project, and place it under Scripts/libs/require.

Defining a Module

The only code we had was the Calculator, residing in the calculator.js file, so go and open the file, and refactor the code to look like the snippet below:


define([
    "jquery"
], function ($) {

    var Calculator = function () {
        this.sum = function (num1, num2) {
            if (num1 == undefined || num2 == undefined) {
                throw "Invalid argument";
            }
            return num1 + num2;
        }
    };

    return Calculator;
});

We've defined Calculator as a module, with a dependency on another module, in this case it's jQuery.
Also, you can see the module return a closure to Calculator.
In some cases you might prefer to return an already instantiated object.

Defining the Bootstrapper

Next, will configure the main entry point for Require.JS, so go and append a JavaScript file under the Scripts library, in the root, and call it bootstrapper-js-test-driver.js.

Open the file and copy the code snippet below:
require.config({
    
    // jsTestDriver serves all files from a test directory, so base URL must reflect it by
    // a relative /test prefix.
    baseUrl: "/test/Scripts",

    paths: {
        jquery: "libs/jquery/jquery-1.8.3.min",
        domReady: "libs/require/domReady",
        text: "libs/require/text",
        order: "libs/require/order"        
    }

});

require([
    "jquery"        
], function ($) {
    
    // Prevent Fake Timers by default.    
    sinon.config.useFakeTimers = false;

});

Pay attention to the inline comment, regarding the /test prefix.
Remove any unused libraries from the paths section.

At this point, after adding the libraries and bootstrapper, your project structure is supposed to look similar to the one in the screenshot below:

visual studio solution explorer

Define the Unit Test as a Module

Next, open the calculator-tests.js file, and also wrap it as a module.
The code should be similar to the code snippet below:

require([
    "jquery",    
    "modules/calculator"
], function ($, Calculator) {

    module("Calculator Tests", {
        setup: function () {    
        },    
        teardown: function () {    
        }
    });

    test("Sum", function () {
        // Arrange
        var calculator = new Calculator();
        // Act
        var sum = calculator.sum(2, 3);
        // Assert
        equal(5, sum);
    });

});

As you can see, as opppsed to other modules, test modules should be defined with require, and not with define, as they are running as standalone modules, and acts as an entry point for jsTestDriver.

You can see that jQuery and the Calculator module, are now being passed as dependencies.

Configuring JS-Test-Driver

Next, load jsTestDriver.conf, and modify it:

  • Under the load section, add an instruction to load Require.JS, and the boostrapper.
  • Add another section , serve, this section is used for static resources.
  • Move calculator.js from the load to the serve section.

After the modification it should look like this:


server: http://localhost:9876

basepath: ""

load:      
  - Scripts/libs/jquery/jquery-1.8.3.min.js
  - Scripts/libs/qunit/qunit-1.10.0.js
  - Scripts/libs/sinon/sinon-1.5.0.js
  - Scripts/libs/sinon/sinon-qunit-1.0.0.js
  - Scripts/libs/sinon/sinon-ie-1.5.0.js
  - Scripts/libs/js-test-driver/equiv.js
  - Scripts/libs/js-test-driver/QUnitAdapter.js
      
  - Scripts/libs/require/require.js
  - Scripts/bootstrapper-js-test-driver.js

test:    
  - Scripts/tests/calculator-tests.js
  
serve:
  - Scripts/modules/calculator.js

Go on and run the tests, and make sure it all works as expected.
And that's it, you've put all your code, both source and tests, to act as independent, decoupled modules.

Summary

You should now be comfortable with writing and running unit tests in Visual Studio, with the knowledge and the confident that you can leverage all your unit testing skills and apply them without any compromise or any limitation set by JavaScript or Visual Studio.

Are you still here? What are you waiting for? Go and write some tests...

4 comments:

  1. Did yoy know about Chutzpah https://chutzpah.codeplex.com/ ?

    ReplyDelete
    Replies
    1. Yes, I've taken a look at it in the past. It's a .NET based JavaScript Test Runner. Never used it though...
      Did you know it's a word in Hebrew? It means 'Nerve'. :)
      Have you tried it?

      Delete
  2. Hi Yaniv!
    Nice posts series. But it looks not so convenient to use Java as runner.
    Did you tried Resharper runner instead? It would be nice to adopt your experience to use together with Resharper's runner.

    ReplyDelete
    Replies
    1. Thanks.
      After setting up the environment, it's seamless that your're working with a Java Runner, as you run it from inside the Studio.
      Does Resharper allow headless tests? Can it be incorporated in a build Server?

      Delete