Using mocha and sinon for client side testing

Returning to the MotorDB project, https://github.com/thesoftwaredude/MotorDB, I have unit tests of the controllers, but nothing for testing the client side code.

I have been reading recently about the various javascript testing frameworks, and decided that I want to try using mocha for testing framework and sinonjs for mocking.
Mocha allows you to use two styles for testing, but I will be using the bdd style.

I looked into what was available via NuGet, but the few packages I tried where running off of controllers. They seemed more suitable for running inside the actual web application itself, which do not want to do. I want to keep the testing simple as end goal of this is to have these tests running is a Continuous Integration tool, and using PhantomJS to run the javascript tests from the command line.

So started from scratch and downloaded the relevant files needed.

  • mocha.js
  • mocha.css
  • chai.ja
  • sinon.js
  • sinon-server.js

All that I needed now was to create a basic HTML page

The HTML needed is

<html>
<head>
 <meta charset="utf-8">
 <title>Mocha Tests</title>
 <link rel="stylesheet" href="mocha.css" />
</head>
<body>
    <script src="mocha.js"></script>
    <script src="chai.js"></script>
    <script src="sinon.js"></script>
    <script src="sinon-server-1.9.0.js"></script>
    <script src="../lib/knockout-2.3.0.js"></script>
    <script src="../lib/jquery-1.10.2.js"></script>
    <script src="../script/DataService.js"></script>
    <script src="../script/Policy.js"></script>
    <script>
        mocha.setup({ ignoreLeaks: true, ui: 'bdd'});
    </script>
    <script>expect = chai.expect;</script>
    <script>
    </script>
    <div id="mocha"></div>
    <script src="../script/policytests.js"></script>
    <script>
        mocha.run();
    </script>
</body>
</html>

There are the references to mocha, knockout, jquery and the javascript code that I want to test, Policy.js

The folder structure that I created with the test project is

javascript
|-content
    |-chai.js
    |-index.html
    |-mocha.css
    |-mocha.js
    |-sinon-server-1.9.0.js
    |-sinon.js
|-script
    |- policytests.js

Within the script folder will be all my relevant test files.
I have also created a post build event on the test project so that all the javascript files are copied to the script folder for testing

xcopy $(SolutionDir)MotorDB.UI\Scripts\app\*.js $(ProjectDir)javascript\script

I want to test the policy download file. Currently the file in typescript is

/// <reference path="../typings/knockout/knockout.d.ts" />
/// <reference path="../typings/knockout.mapping/knockout.mapping.d.ts" />
/// <reference path="DataService.ts" />

module MotorDB {
    export class Policy {
        constructor(
            public Identifier: number,
            public PolicyNumber: string,
            public Policyholder: string) {
         }
    }

    export class PolicyViewModel {
         private dataService: MotorDB.DataService;
         public Policies: KnockoutObservableArray<Policy>;

        constructor() {
             var self = this;
             self.dataService = new MotorDB.DataService();
             self.Policies = ko.observableArray<Policy>();
        }

        public Load() {
            var self = this;
            self.dataService.GetAllPolicys()
                .done(function (data) {
                    $.each(data, function (index, element) {
                    var policy = new Policy(element.Identifier, element.PolicyNumber, element.PolicyholderName);
                    self.Policies.push(policy);
                });
            })
        }
    }
}

Which produces the following javascript:

var MotorDB;
(function (MotorDB) {
    var Policy = (function () {
        function Policy(Identifier, PolicyNumber, Policyholder) {
            this.Identifier = Identifier;
            this.PolicyNumber = PolicyNumber;
            this.Policyholder = Policyholder;
        }
        return Policy;
    })();
    MotorDB.Policy = Policy;

    var PolicyViewModel = (function () {
        function PolicyViewModel() {
            var self = this;
            self.dataService = new MotorDB.DataService();
                self.Policies = ko.observableArray();
        }
        PolicyViewModel.prototype.Load = function () {
            var self = this;
            self.dataService.GetAllPolicys().done(function (data) {
                $.each(data, function (index, element) {
                    var policy = new Policy(element.Identifier, element.PolicyNumber, element.PolicyholderName);
                    self.Policies.push(policy);
                });
            });
        };
        return PolicyViewModel;
    })();
    MotorDB.PolicyViewModel = PolicyViewModel;
})(MotorDB || (MotorDB = {}));

Before starting on the tests, some refactoring of the javascript code is required. As I am looking to mock out the database call, just like in C#, I will need to inject into the PolicyViewModel class an instance of the DataService.

In the typescript files, the first we need to do is extract this class into an interface and then use this interface instead. Now in C# this is simple operation to do with Resharper, but within typescript, I have discovered that all of the work has to be completed manually.

First the interface definition required

    export interface IDataService {
        GetAllPolicys();
    }

Now to update the DataService so that it implements the interface

export class DataService implements IDataService

Next, modify PolicyViewModel class to pass in the DataService object instead of the code using the concrete creation

constructor(dataService: IDataService) {
    var self = this;
    self.dataService = dataService;
    self.Policies = ko.observableArray<Policy>();
}

With this in place, we can now return to completing the tests.

I have to admin the next part is where I really struggled. I was thinking like I do in C#, and wanted to mock out the data repository (MotorDB.js) and return the expected data. But in jQuery there is no way to do a synchronous data call. All ajax calls in jQuery are asynchronous, and when you make an ajax call, you are returned a promise. not the actual data.

I have been struggling to mock out the MotorDB class to get sinon to return a promise for the data. No matter what I tried, I kept on getting sinon parsing errors.

I have an open question on stackoverflow, but at the time of writing this post, no answers yet.

So I had to fall back to use sinon’s fake http server. I got working tests, but I don’t want to rely on this going forward, so I will keep working on trying to get sinon so that I can mock out the MotorDB class, and return a promise that contains the expected data.

Sinon has FakeHTTPRequest, and fakeServer. As per the documentation, the fake server is a High-level API to manipulate `FakeXMLHttpRequest` instances.

I created two tests, one for FakeHTTPRequest, and fake server. Both tests are shown below

var fakeDataResponse = [{
    Identifier: 1,
    PolicyholderName: "Policyholder 1",
    PolicyNumber: "1001",
    PolicyPeriods: [{
        StartDate: "2011-02-01",
        EndDate: "2012-01-31"
    },
        {
            StartDate: "2012-02-01",
            EndDate: "2013-01-31"
        },
        {
            StartDate: "2013-02-01",
            EndDate: "2014-01-31"
        }]
}];

describe('test populating Policy data using sinon.useFakeHttpRequest', function () {
    var requests;
    beforeEach(function () {
        this.xhr = sinon.useFakeXMLHttpRequest();
        requests = this.requests = [];
        this.xhr.onCreate = function (xhr) {
            requests.push(xhr);
        };
    });

    afterEach(function () {
        this.xhr.restore();
    });

    it('Get Policy Data ', function () {
        var db = new MotorDB.DataService();
        var sut = new MotorDB.PolicyViewModel(db);
        sut.Load();
        requests[0].respond(200, { "Content-Type": "application/json" }, JSON.stringify(fakeDataResponse));
        var policyData = sut.Policies();
        expect(policyData.length).to.equal(1);
    });
});

describe('Populate Policy Data using sinon.fakeServer', function() {
    var server;
    beforeEach(function () {
        server = sinon.fakeServer.create();
        server.respondWith([200, { "Content-Type": "application/json" }, JSON.stringify(fakeDataResponse)]);
    });
    afterEach(function () {
        server.restore();
    });
    it('Get Policy Data', function() {
        var db = new MotorDB.DataService();
        var sut = new MotorDB.PolicyViewModel(db);
        sut.Load();
        server.respond();
        var policyData = sut.Policies();
        expect(policyData.length).to.equal(1);
    });
});

When using sinon.server you always to to restore what you are mocking to its original function, just in case it will interfere with other tests. In my scenario, this is not a problem buts its a good practice to follow.

Advertisements

My musing about anything and everything

Tagged with: , , , ,
Posted in mocha, sinon, TDD, Typescript

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 11 other followers

%d bloggers like this: