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
Automated JavaScript Testing
Notes used for this talk: https://gist.github.com/wildlyinaccurate/6f3bc2e0c2de8bf0b457
- 3,160