Thursday, June 5, 2014

Use Intern for JavaScript unit testing

With so much of JavaScript in use on and off the web and a growing library where I work, there is a need for good unit testing.  I have spent time looking at many of the different tools, though they have not been quite adequate for all of our needs.  I have recently been pointed towards Intern, a new project by SitePen Labs.

Here I am going to provide an overview of Intern since SitePen Labs has done a good job of providing a step by step tutorial for testing a simple 'hello world' script, https://github.com/theintern/intern-tutorial.

Intern tries to combine various other tools along with its own framework to pull together a single framework for unit testing and functional testing JavaScript.  By using something like Sauce Labs or Selenium with the various browser WebDriver you can test your code against all your supported browsers. The assertion library that Intern is packaged with is Chai.js. The Chai Assertion Library has 3 different interfaces for assertions that you can choose from.
There is 'assert' that provides TDD styled assertions and is similar to that of JUnit.
assert('foo' !== 'bar', 'foo is not bar');
assert.equal(3, '3', '== coerces values as strings');
assert.isTrue(true, 'boolean true');

Chai also provides for BDD style assertions using 'expect'/'should'.
expect(true).to.be.true;
expect(foo).to.exist;
expect('hello').to.equal('hello');


Intern provides a number of different reporters, some of which allow integration with other tools like TeamCity and Cobertura.  Reporting is also extensible by allowing for easy custom reporters to be created and used.  For code coverage reporting Intern is packaged with istanbul, another popular javascript library.

Intern is built on the Dojo framework using AMD loaders (which can be changed to use other loaders like RequireJS).  Its also packaged as a node.js module so it can be executed in non-browser environments.  As well as having a simple installation using npm
cd projectroot
npm install intern --save-dev


Intern has a single configuration file that you customize for your tests.  It is packaged with a default config that you can copy and customize (such as providing the name of your test scripts).
define({
    ...
    suites: [ /* 'myPackage/tests/foo', 'myPackage/tests/bar' */ ],
    ...
});

Writing tests with Intern is rather straight forward as well.   Intern provides 3 modes for testing, TDD,
define([
    'intern!tdd',
    'intern/chai!assert',
], function(tdd, assert)
{
    with(tdd)
    {
        suite('simple', function()
        {
            before(function() {});
            beforeEach(function() {});
            after(function() {});
            test('#simpleAssert', function()
            {
                 assert.isTrue(true);
            });
        });
    });
});

BDD,
define([
    'intern!bdd',
    'intern/chai!assert',
], function(bdd, assert)
{
    with(bdd)
    {
         describe('simple', function()
         {
            before(function() {});
            beforeEach(function() {});
            after(function() {});
            it('#simpleAssert', function()
            {
                 assert.isTrue(true);
            });
         });
     });
});

and Object.
define([
    'intern!object',
    'intern/chai!assert',
], function(object, assert)
{
    registerSuite(
    {
        setup: function() {};
        beforeEach: function() {};
        teardown: function() {};
        '#simpleAssert': function()
        {
             assert.isTrue(true);
        }
    });
});

They all have a similar pattern, just with a different intern plugin to allow for that mode.  Intern also provides for functional testing.
...
'submit form': function ()
{
    return this.remote
        .get(require.toUrl('./someform.html'))
        .elementById('operation')
            .click()
            .type('hello, world')
        .end()
        .elementById('submit')
            .click()
        .end()
        .waitForElementById('result')
            .text()
        .then(function(resultText)
        {
            assert.ok(resultText.indexOf('"hello, world"') > -1, 'failed');
        });
}
...


Intern is a fairly new library, only introduced in early 2013, but its contributors are active and participate in providing support through Stack Overflow.  On the projects road map is providing a packaged mocking framework to facilitate focused unit testing.  There is a lot of information on how to provide mocking in your tests but no practices settled on or packaged.  For this we are left to provide mocking through patterns and libraries on our own.

Other than mocking, Intern so far has shown that it should be able to provide most of what I need. I will be working toward converting and using Intern on our JavaScript library where I work.  Part of the process will include integrating it into our continuous integration process using Jenkins.  From what I have seen, this should not be too difficult.


Resource Links:




No comments:

Post a Comment