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
5. What About ...
• Asynchronous goodness
• Interacting with teh DOMz
• Evil Legacy Code
• Continuous Integration
• Clean readable tests that reflect your domain
Thursday, 19 July 12
8. 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
9. 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
11. 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
13. 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
14. 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
15. Wait, There’s More ...
• expect(spy).not.toHaveBeenCalled()
• createSpy().andReturn(something)
• createSpy().andCallFake(function() {})
• createSpy().andCallThrough()
Thursday, 19 July 12
16. 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
17. ... 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
18. 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
19. 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
20. 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
21. 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
22. 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
23. 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
24. 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
25. 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
26. If Dependencies Aren’t
Injected ...
var LegacySomething = function() {
this.doSomething = function() {
var dependency = new Dependency();
dependency.method();
};
};
Thursday, 19 July 12
27. 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
30. Maven
> mvn clean test
http://searls.github.com/jasmine-maven-plugin/
Thursday, 19 July 12
31. Node.js
> jasmine-node specs/
https://github.com/mhevery/jasmine-node
Thursday, 19 July 12
32. 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
33. 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
34. 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
35. 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