Automated JavaScript Testing at IPC Media

(function($) {
  $.ajax({
    url: '/foo',
    type: 'POST',
    data: {
      foo: 'bar'
    },
    success: function(response) {
      return $('.foo').text(response.message).show();
    }
  });
})(jQuery);

Mocha

Chai

Sinon.JS

Mocha


describe('Array', function() {
    describe('.push()', function() {
        it('should append a value', function() {
            // ...
        });
    });
});

Chai


// BDD-style "should" assertions
foo.should.equal('bar');

// BDD-style "expect" assertions
expect(foo).to.be.a('string');

// TDD-style assertions
assert.typeOf(foo, 'string', 'foo is a string');

Sinon.JS


sinon.stub(jQuery, 'ajax');

jQuery.ajax({ url: '/foo/bar' });

assert(jQuery.ajax.calledWithMatch({ url: '/foo/bar' }));

var clock = sinon.useFakeTimers();
var div = $('#foo'); // div.css('width') -> '100px'

div.animate({ width: '200px' }, 500);

assertEquals('100px', div.css('width'));
clock.tick(501);
assertEquals('200px', div.css('width'));

foo.js

(function($) {
  $.ajax({
    url: '/foo',
    type: 'POST',
    data: {
      foo: 'bar'
    },
    success: function(response) {
      return $('.foo').text(response.message).show();
    }
  });
})(jQuery);

foo.js

(function($) {
  window.ipc.foo.bar = function() {
    $.ajax({
      url: '/foo',
      type: 'POST',
      data: {
        foo: 'bar'
      },
      success: function(response) {
        return $('.foo').text(response.message).show();
      }
    });
  };
  
  window.ipc.foo.bar();
})(jQuery);

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);
    
    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    $mock.expects('get').once();

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once()
      .withArgs(sinon.match({
        type: 'POST'
      }))
      .yieldsTo('success', {
        message: 'Hello'
      });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once()
      .withArgs(sinon.match({
        type: 'POST',
        url: '/foo',
        data: sinon.match({ foo: 'bar' })
      }))
      .yieldsTo('success', {
        message: 'Hello'
      });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

foo.js


    (function($) {
  window.ipc.foo.bar = function() {
    $.ajax({
      url: '/foo',
      type: 'POST',
      data: {
        foo: 'bar'
      },
      success: function(response) {
        return $('.foo').text(response.message).show();
      }
    });
  };
  
  window.ipc.foo.bar();
})(jQuery);

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    var $node = {
      text: function() {},
      show: function() {}
    };

    // text() will return $node to allow method chaining
    sinon.stub($node, 'text').returns($node);
    sinon.stub($node, 'show');

    // Force $() to return our $node stub
    sinon.stub(window, '$').returns($node);

    window.ipc.foo.bar();

    $mock.verify();
  });
});

test.js

describe('window.ipc.foo.bar()', function() {
  it('should POST somewhere and show a message', function() {
    var $mock = sinon.mock(jQuery);

    $mock.expects('ajax').once().yieldsTo('success', {
      message: 'Hello'
    });

    var $node = {
      text: function() {},
      show: function() {}
    };

    // text() will return $node to allow method chaining
    sinon.stub($node, 'text').returns($node);
    sinon.stub($node, 'show');

    // We can stub window.$ (i.e. $) so that it returns $node
    sinon.stub(window, '$').returns($node);

    window.ipc.foo.bar();
    
    sinon.assert.calledOnce($node.show);
    sinon.assert.calledOnce($node.text);
    sinon.assert.calledWith($node.text, 'Hello');

    $mock.verify();
  });
});



+ Karma


package.json

{
  "name": "foo-project",
  "description": "For fooing.",
  "version": "0.0.1",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-cli": "~0.1.11",
    "grunt-karma": "~0.7.1",
    "karma-mocha": "~0.1.0",
    "karma-chai": "~0.0.1",
    "karma-sinon": "~0.0.1",
    "load-grunt-tasks": "~0.2.0"
  }
}

Gruntfile.js

module.exports = function (grunt) {
  require('load-grunt-tasks')(grunt);

  grunt.initConfig({
    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true
      }
    }
  });

  grunt.registerTask('default', ['karma']);
};

karma.conf.js

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai', 'sinon'],

    files: [
        'http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js',
        'lib/*.js',
        'tests/*.js'
    ],

    browsers: ['PhantomJS']
  });
};

karma.conf.js

{
    browsers: ['PhantomJS']
}

  • Chrome
  • ChromeCanary
  • Safari
  • Firefox
  • Opera
  • PhantomJS
  • IE

karma.conf.js

{
    browsers: ['PhantomJS', 'Chrome', 'Firefox', 'IE']
}

  • Chrome
  • ChromeCanary
  • Safari
  • Firefox
  • Opera
  • PhantomJS
  • IE


package.json

{
  "name": "foo-project",
  "description": "For fooing.",
  "version": "0.0.1",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-cli": "~0.1.11",
    "grunt-karma": "~0.7.1",
    "karma-mocha": "~0.1.0",
    "karma-chai": "~0.0.1",
    "karma-sinon": "~0.0.1",
    "load-grunt-tasks": "~0.2.0"
  }
}

package.json

{
  "name": "foo-project",
  "description": "For fooing.",
  "version": "0.0.1",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-cli": "~0.1.11",
    "grunt-karma": "~0.7.1",
    "karma-mocha": "~0.1.0",
    "karma-chai": "~0.0.1",
    "karma-sinon": "~0.0.1",
    "load-grunt-tasks": "~0.2.0",
    "grunt-contrib-jshint": "~0.6.3"
  }
}

package.json

{
  "name": "foo-project",
  "description": "For fooing.",
  "version": "0.0.1",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-cli": "~0.1.11",
    "grunt-karma": "~0.7.1",
    "karma-mocha": "~0.1.0",
    "karma-chai": "~0.0.1",
    "karma-sinon": "~0.0.1",
    "load-grunt-tasks": "~0.2.0",
    "grunt-contrib-jshint": "~0.6.3",
    "karma-junit-reporter": "~0.1.0"
  }
}

package.json

{
  "name": "foo-project",
  "description": "For fooing.",
  "version": "0.0.1",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-cli": "~0.1.11",
    "grunt-karma": "~0.7.1",
    "karma-mocha": "~0.1.0",
    "karma-chai": "~0.0.1",
    "karma-sinon": "~0.0.1",
    "load-grunt-tasks": "~0.2.0",
    "grunt-contrib-jshint": "~0.6.3",
    "karma-junit-reporter": "~0.1.0",
    "karma-coffee-preprocessor": "~0.1.0"
  }
}

Automated JavaScript Testing

By Joseph Wynn