Pages

Tuesday, February 12, 2013

JavaScript Unit Testing in Visual Studio Part II

Setting up the Environment for Headless Tests

js test driver
In the previous post, we've set up the scene, by setting the expectations and picking the libraries & tools for the job.

In this post we will take it step-by-step, so you can follow and get active in the process.

So here we go... 




Prerequisites


Set Up the Project

  • We'll start by creating an empty Web Project. So, launch your Visual Studio, and create a new Web Project. Select ASP.NET MVC Web Application, name it JavaScriptTesting or whatever makes you happy, and make sure to select an Empty template on the second step in the Wizard.
  • Feel free to delete all items under the Content and Scripts project folders.
    Go even further, and delete the packages.config file automatically added by Visual Studio and the packages folder in the solution root on the disk.
    Just so we can concentrate on what really matters... (Nothing personal against NuGet).
  • Under the Scripts folder,go and create three sub-folders and name them: libs, modules, and tests.

At the moment your solution tree should look similar to the one in the screenshot below.
Nothing special yet, a naked MVC project with minimal elements.

visual studio solution explorer
NOTE: In your development environment you might not like the idea of having the source code and tests under the same project. You can of course place the tests in a separate project, and as the concept of adding a reference to a js file doesn't exist, and we would not want to duplicate source files, you can accomplish that by defining a pre-build event on the tests project to copy all the stuff you wanna test.

In this post, for brevity, a single project holding both source and tests will suffice...

Download the Libraries 

Let's go and grab all the libraries.

QUnit
  • QUnit - Get both js and css, and place them under a Scripts/libs/qunit.
Sinon.JS
  • Sinon.JS - Save the js under a sinon folder beneath Scripts/libs/sinon.
  • Sinon Adapter for QUnit - Save it also under the sinon folder.
  • Make sure to also get the Sinon-IE.js. It is for compatibility for IE when Timers & fake XHR is used. While on Sinon.JS site, navigate to the Fake XMLHttpRequest section under the documentation and look for sinon-ie.js.
JSTD
jQuery
  • Get a fresh copy of jQuery and also place it under a new folder - Scripts/libs/jquery.
At the moment your solution tree should look similar to the one in the screenshot below, and your solution folder should contain the project folder and the JSTD jar file.


visual studio solution explorer


We will leave Require.js aside for now, to be discussed in a later post in this series.
So, for now, we've got all the libraries we need to start with. 


The Unit Under Test

In order to run unit tests, we first need some kind of unit. So let's create a very simple component to play around with. 

Create a new calculator.js file, under Scripts/modules, with the code snippet below:

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

This should be enough, as now we can go on and test the sum method.


Unit Test

Next, create a new js file under Scripts/tests and name it calculator-tests.js, with the code snippet below:

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);
});

We've created a Module named Calculator Tests, with a single test named Sum, which in we instantiate the Calculator, call the Sum method, and simply Asserts for the expected result.

Configuring JS-Test-Driver

Js-Test-Driver, expects a configuration file in a YAML format. For more about the Configuration File settings see here.

Create a text file in Visual Studio, name it jsTestDriver.conf, and place it in the project root, just next to the Js-Test-Driver jar file.

Copy the snippet below to the file:


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/modules/calculator.js

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

If you've placed JsTestDriver outside the project, replace the basepath to reflect the path to the folder containing JsTestDriver-1.3.5.jar.
If using other versions of the libraries under the load section, then rename accordingly.


Customize the Tools Menu

We're almost there...

JS-Test-Driver can be executed from the command line, but we wanted seamless integration with Visual Studio, so let's create custom menu entries so we can run those tests with no more than a few keystrokes.

Menu Item for launching the Server

  • Open the External Tools under the Tools menu.
  • Click Add, to add a new entry in the menu.
  • Set "JSTD Server" for the Title.
  • Set the full path to jave.exe.
    In my case it is "C:\Program Files (x86)\Java\jre7\bin\java.exe".
  • Set the Arguments to:
    -jar JsTestDriver-1.3.5.jar --port 9876 --browser "C:\Program Files (x86)\Internet Explorer\iexplore.exe","C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

    I've set it to run both IE and Chrome.
    Make sure to point to the correct path on your system.
  • Set Initial Directoryto $(ProjectDir)
  • Check Close on exit.
  • Click OK to Save.

It should look similar to the screenshot below:


visual studio external tools


Menu Item for running Tests

  • Open the External Tools under the Tools menu.
  • Click Add, to add a new entry in the menu.
  • Set "JSTD &Run" for the Title.
  • Set the full path to jave.exe.
    In my case it is "C:\Program Files (x86)\Java\jre7\bin\java.exe".
  • Set the Arguments to:
    -jar JsTestDriver-1.3.5.jar --server http://localhost:9876 --tests all --testOutput "jsTestDriver-TestResults" --verbose
  • Set Initial Directory to $(ProjectDir)
  • Check Use Output window.
  • Save settings.

It should look similar to the screenshot below:
visual studio external tools


And that's it.

Running The Tests

  • From the Tools menu, click on JSTD Server.
    It will launch a new command, and you'll see new windows/tabs added to both Chrome and IE.
  • Now go and hit Tools menu, and then JSTD Run, or  just Alt+T+R.
NOTE: When the solution contains more than one project, make sure to select the project before running the JSTD Server or the tests. As the $(ProjectDir) will instruct Visual Studio to look for the the Jar file under the selected project.

Tests will get executed, and you'll be able to see the results in the Output Window:

visual studio output window
You've made it. You have just executed your first JavaScript Unit Tests in Visual Studio.
And you can see that both tests passed.

You should leave the JSTD Server running, and the browsers waiting, and just hit Alt+T+R when changing the code...


What We've accomplished

  • Seamless integration with Visual Studio.
  • Headless testing.Against multiple browsers at the same time. 
  • Support for Async Tests.
    (More about it in future post).
  • Documented libraries & Tools.
  • Free Tools.

What's Next?

In the next posts we will talk about AMD and we will see how to combine JS-Test-Driver and Require.JS to play nicely together.

Have fun testing...

2 comments:

  1. I always had to specify --basePath "$(ProjectDir)" when configuring both JSTD Server and Client in External Tools menu

    ReplyDelete
    Replies
    1. Hi Nikolay, if it works for you then it sounds like another valid way for configuration.
      I set the initial directory to $(ProjectDir), and inside jsTestDriver.conf I set the --basePath in to an empty string, as you can in a previous post, number 2 in this series.

      Delete