The document discusses using the JavaScript testing framework Jasmine. It provides examples of writing tests using Jasmine's BDD syntax and matchers. It also covers best practices like testing asynchronous code using spies, setting up fixtures for DOM testing, extending Jasmine with custom matchers, and considerations for continuous integration and legacy code testing.
1 of 57
More Related Content
Testing javascriptwithjasmine ddd-sydney
1. Testing JavaScript
LIKE A BOSS
Jo Cranford
@jocranford
Thursday, 19 July 12
3. BDD With Jasmine Is
Awesome Sauce
describe("Score Calculation Behaviour", function() {
it("should score 0 when no pins are knocked down", function() {
var game = new BowlingGame(10);
game.roll(0);
expect(game.score()).toBe(0);
});
});
Thursday, 19 July 12
4. BDD With Jasmine Is
Awesome Sauce
describe("Score Calculation Behaviour", function() {
it("should score 0 when no pins are knocked down", function() {
var game = new BowlingGame(10);
game.roll(0);
expect(game.score()).toBe(0);
});
});
Thursday, 19 July 12
5. BDD With Jasmine Is
Awesome Sauce
describe("Score Calculation Behaviour", function() {
it("should score 0 when no pins are knocked down", function() {
var game = new BowlingGame(10);
game.roll(0);
expect(game.score()).toBe(0);
});
});
Thursday, 19 July 12
6. BDD With Jasmine Is
Awesome Sauce
describe("Score Calculation Behaviour", function() {
it("should score 0 when no pins are knocked down", function() {
var game = new BowlingGame(10);
game.roll(0);
expect(game.score()).toBe(0);
});
});
Thursday, 19 July 12
10. Maven
? Searls Jasmine Maven plugin
? Add it to the pom to run tests within the test lifecycle
phase
> mvn jasmine:bdd
http://searls.github.com/jasmine-maven-plugin/
Thursday, 19 July 12
12. Standalone
? Download the ?les and copy them all across to your
project.
? Edit the SpecRunner.html to include your JavaScript
source and tests.
? Open in your favourite browser.
https://github.com/pivotal/jasmine/downloads
Thursday, 19 July 12
13. Now Let¡¯s Write A Test
? describe("Score Calculation Behaviour", function() {
it("should score 0 when no pins are knocked down", function() {
var game = new BowlingGame(10);
game.roll(0);
expect(game.score()).toBe(0);
});
});
Thursday, 19 July 12
15. .toContain()
describe("How to test for items in an Array", function() {
it("should tell me if the array contains an item", function() {
var theArray = [1, 2, 3];
expect(theArray).toContain(1);
expect(theArray).not.toContain(4);
});
});
Thursday, 19 July 12
16. .toThrow()
it("should accept a single digit at a time", function() {
expect(function() {
calculator.enterDigit("2");
}).not.toThrow();
});
it("should not accept letters", function() {
expect(function() {
calculator.enterDigit("a");
}).toThrow("Only accepts numbers");
});
Thursday, 19 July 12
17. .toMatch()
it("should compare to a regex", function () {
expect("@jocranford").toMatch("@([A-Za-z0-9_]+)");
});
Thursday, 19 July 12
18. Before And After
beforeEach(function() {
fakeFrame = {
addRoll: jasmine.createSpy("Add roll"),
isComplete: jasmine.createSpy("Is complete"),
setSpareBonus: jasmine.createSpy("spare bonus"),
setStrikeBonus: jasmine.createSpy("strike bonus"),
score: jasmine.createSpy("score")
};
});
Thursday, 19 July 12
20. What About ...
? Asynchronous goodness
? Interacting with teh DOMz
? Evil Legacy Code
? Continuous Integration
? Clean readable tests that re?ect your domain
Thursday, 19 July 12
23. The JavaScript Code
var Presentation = function() {
this.presenters = [];
};
Presentation.prototype.loadPresenters = function() {
var presenters = this.presenters;
$.getJSON("people.json", function(data) {
$.each(data, function(idx, person) {
presenters.push(person);
});
});
};
Thursday, 19 July 12
24. Easy, Right?
describe("How not to test an asynchronous function", function
() {
it("should load the presenters", function () {
var presentation = new Presentation();
presentation.loadPresenters();
expect(presentation.presenters.length).toBe(2);
});
});
Thursday, 19 July 12
26. But This Might Work ...
describe("Still not ideal though", function () {
it("should load the presenters", function () {
spyOn($, "getJSON").andCallFake(function (url, callback) {
callback([{},{}]);
})
var presentation = new Presentation();
presentation.loadPresenters();
expect(presentation.presenters.length).toBe(2);
});
});
Thursday, 19 July 12
28. Spy On An Existing Method
it("can spy on an existing method", function() {
var fakeElement = $("<div style='display:none'></div>");
spyOn(fakeElement, 'show');
var toggleable = new Toggleable(fakeElement);
toggleable.toggle();
expect(fakeElement.show).toHaveBeenCalled();
});
Thursday, 19 July 12
29. Spy On An Existing Method
it("can create a method for you", function() {
var fakeElement = {};
fakeElement.css = function() {};
fakeElement.show = jasmine.createSpy("Show spy");
var toggleable = new Toggleable(fakeElement);
toggleable.toggle();
expect(fakeElement.show).toHaveBeenCalled();
});
Thursday, 19 July 12
30. Wait, There¡¯s More ...
? expect(spy).not.toHaveBeenCalled()
? createSpy().andReturn(something)
? createSpy().andCallFake(function() {})
? createSpy().andCallThrough()
Thursday, 19 July 12
31. Spy On The Details
? expect(spy).toHaveBeenCalled()
? expect(spy.callCount).toBe(x)
? expect(spy).toHaveBeenCalledWith()
? Tip: use jasmine.any(Function/Object) for parameters
you don¡¯t care about
Thursday, 19 July 12
32. ... And We¡¯re Back.
Sooooo ... spies are great and all,
but what if your callback function
takes a while to run?
Thursday, 19 July 12
33. Don¡¯t Do This At Home.
Presentation.prototype.loadPresentersMoreSlowly = function() {
var preso = this;
$.getJSON("people.json", function(data) {
setTimeout(function() {
$.each(data, function(idx, person) {
preso.presenters.push(person);
});
}, 2000);
});
};
Thursday, 19 July 12
34. Don¡¯t Do This, Either.
it("should have loaded after three seconds, right?", function()
{
spyOn($, "getJSON").andCallFake(function(url, callback) {
callback([{}, {}]);
})
var presentation = new Presentation();
presentation.loadPresentersMoreSlowly();
setTimeout(function() {
expect(presentation.presenters.length).toBe(2);
}, 3000);
});
Thursday, 19 July 12
35. But What If I Just ...
Presentation.prototype.loadPresentersMoreSlowly = function() {
var preso = this;
$.getJSON("people.json", function(data) {
setTimeout(function() {
$.each(data, function(idx, person) {
preso.presenters.push(person);
});
preso.presentersHaveLoaded = true;
}, 2000);
});
};
Thursday, 19 July 12
36. Now Wait, Wait ... RUN!
it("should load the presenters", function() {
spyOn($, "getJSON").andCallFake(function(url, callback) {
callback([{}, {}]);
})
var presentation = new Presentation();
presentation.loadPresentersMoreSlowly();
waitsFor(function() {
return presentation.presentersHaveLoaded;
}, "presenters have loaded");
runs(function() {
expect(presentation.presenters.length).toBe(2);
});
});
Thursday, 19 July 12
37. Testing Interaction With The
DOM
? Do you REALLY need to?
? Tests will have a high maintenance cost
? Instead separate logic from view and test logic
? Use templates for the view
Thursday, 19 July 12
38. Testing Interaction With The
DOM
it("should display the score", function() {
setFixtures("<div id='score'></div>");
var bowlingGameView = new BowlingGameView();
bowlingGameView.showScore(100);
expect($("#score").text()).toBe("Your current score is 100");
});
https://github.com/velesin/jasmine-jquery
Thursday, 19 July 12
39. Legacy (untested)
JavaScript Code
? Long methods
? Violation of Single Responsibility Principle
? Side effects
? Lack of dependency injection
? Lots of new X()
? Unclear intentions
Thursday, 19 July 12
40. Testing Interaction
it("should call the method on the dependency", function() {
var dependency = {};
dependency.method = jasmine.createSpy();
var myObject = new Something(dependency);
myObject.doSomething();
expect(dependency.method).toHaveBeenCalled();
});
Thursday, 19 July 12
41. If Dependencies Aren¡¯t
Injected ...
var LegacySomething = function() {
this.doSomething = function() {
var dependency = new Dependency();
dependency.method();
};
};
Thursday, 19 July 12
42. Create Stubs
it("is a pain but not impossible", function() {
Dependency = function() {};
Dependency.prototype.method = jasmine.createSpy()
var myObject = new LegacySomething();
myObject.doSomething();
expect(Dependency.prototype.method).toHaveBeenCalled();
});
Thursday, 19 July 12
45. Maven
> mvn clean test
http://searls.github.com/jasmine-maven-plugin/
Thursday, 19 July 12
46. Node.js
> jasmine-node specs/
https://github.com/mhevery/jasmine-node
Thursday, 19 July 12
47. Rhino
? Download:
? Rhino (js.jar) from Mozilla
? env.rhino.js from www.envjs.com
? Jasmine console reporter from Larry Myers Jasmine
Reporters project (github)
http://www.build-doctor.com/2010/12/08/javascript-bdd-jasmine/
Thursday, 19 July 12
48. Rhino
load('env.rhino.1.2.js');
Envjs.scriptTypes['text/javascript'] = true;
var specFile;
for (i = 0; i < arguments.length; i++) {
specFile = arguments[i];
console.log("Loading: " + specFile);
window.location = specFile
}
> java -jar js.jar -opt -1 env.bootstrap.js ../SpecRunner.html
Thursday, 19 July 12
49. Extending Jasmine With
Custom Matchers
it("should match the latitude and longitude", function() {
var pointOnMap = { latitude: "51.23", longitude: "-10.14" };
expect(pointOnMap.latitude).toBe("51.23");
expect(pointOnMap.longitude).toBe("-10.14");
});
it("should match the latitude and longitude", function() {
var pointOnMap = { latitude: "51.23", longitude: "-10.14" };
expect(pointOnMap).toHaveLatitude("51.23");
expect(pointOnMap).toHaveLongitude("-10.14");
});
Thursday, 19 July 12
50. Extending Jasmine With
Custom Matchers
it("should match the latitude and longitude", function() {
var pointOnMap = { latitude: "51.23", longitude: "-10.14" };
expect(pointOnMap).toHaveLatLongCoordinates("51.23", "-10.14");
});
Thursday, 19 July 12
52. Custom Failure Messages
toHaveLatitude: function(lat) {
this.message = function() {
return "Expected Latitude " + this.actual.latitude
+ " to be " + lat;
};
return this.actual.latitude === lat;
}
Thursday, 19 July 12
53. Do We Really Need To Test
Everything?
DO: DON¡¯T:
? Test Behaviour and ? Over-test DOM interaction
Functionality
? Test Getters and Setters
? Test the places where the
bad bugs bite ? Test functionality in third
party libraries
? Write tests that are
expensive to maintain
Thursday, 19 July 12
54. Tips & Gotchas
? Tests aren¡¯t completely independent!
? If tests are hard to write, it¡¯s often an indication of a smell
in the code
? Automate creation of SpecRunner.html
? Run tests in different browsers
Thursday, 19 July 12