• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Testing javascriptwithjasmine ddd-sydney
 

Testing javascriptwithjasmine ddd-sydney

on

  • 1,275 views

Slides from my presentation on June 30 at Developer Developer Developer Sydney

Slides from my presentation on June 30 at Developer Developer Developer Sydney

Statistics

Views

Total Views
1,275
Views on SlideShare
1,247
Embed Views
28

Actions

Likes
2
Downloads
9
Comments
0

1 Embed 28

http://lanyrd.com 28

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Testing javascriptwithjasmine ddd-sydney Testing javascriptwithjasmine ddd-sydney Presentation Transcript

    • Testing JavaScript LIKE A BOSS Jo Cranford @jocranfordThursday, 19 July 12
    • Y U NO JAVASCRIPT TESTS?Thursday, 19 July 12
    • 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
    • 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
    • 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
    • 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
    • Basic ShizzleThursday, 19 July 12
    • Jasmine In Your Project • Ruby Gem • Maven • Node.js • StandaloneThursday, 19 July 12
    • Ruby Gem > jasmine init > rake jasmine https://github.com/pivotal/jasmine-gemThursday, 19 July 12
    • 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
    • Node.js > npm install jasmine-node -g > jasmine-node specs/ https://github.com/mhevery/jasmine-nodeThursday, 19 July 12
    • Standalone • Download the files 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/downloadsThursday, 19 July 12
    • 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
    • • expect(game.score()).not.toBe(1); expect(true).toBeTruthy(); expect(false).toBeFalsy(); expect(undefined).toBeUndefined(); expect({}).toBeDefined(); expect(null).toBeNull(); expect(1).toBeLessThan(2); expect(2).toBeGreaterThan(1); expect(new Date(2012, 1, 1).toBeLessThan(new Date(2012, 2, 1)); expect("aaa").toBeLessThan("bbb");Thursday, 19 July 12
    • .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
    • .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
    • .toMatch() it("should compare to a regex", function () { expect("@jocranford").toMatch("@([A-Za-z0-9_]+)"); });Thursday, 19 July 12
    • 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
    • Is JavaScript Ever Really That Simple?Thursday, 19 July 12
    • What About ... • Asynchronous goodness • Interacting with teh DOMz • Evil Legacy Code • Continuous Integration • Clean readable tests that reflect your domainThursday, 19 July 12
    • Approaches To Testing Asynchronous CodeThursday, 19 July 12
    • Let’s Load Some JSON [ { "firstName": "Jo", "lastName": "Cranford", "company": "Atlassian" }, { "firstName": "Rachel", "lastName": "Laycock", "company": "ThoughtWorks" } ]Thursday, 19 July 12
    • 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
    • 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
    • Well ... Not So Much.Thursday, 19 July 12
    • 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
    • A Little Detour ...Thursday, 19 July 12
    • 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
    • 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
    • Wait, There’s More ... • expect(spy).not.toHaveBeenCalled() • createSpy().andReturn(something) • createSpy().andCallFake(function() {}) • createSpy().andCallThrough()Thursday, 19 July 12
    • 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 aboutThursday, 19 July 12
    • ... 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
    • 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
    • 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
    • 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
    • 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
    • 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 viewThursday, 19 July 12
    • 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-jqueryThursday, 19 July 12
    • Legacy (untested) JavaScript Code • Long methods • Violation of Single Responsibility Principle • Side effects • Lack of dependency injection • Lots of new X() • Unclear intentionsThursday, 19 July 12
    • 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
    • If Dependencies Aren’t Injected ... var LegacySomething = function() { this.doSomething = function() { var dependency = new Dependency(); dependency.method(); }; };Thursday, 19 July 12
    • 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
    • Continuous Integration • Ruby Gem • Maven • Node.js • Rhino (Java)Thursday, 19 July 12
    • Ruby Gem require jasmine load jasmine/tasks/jasmine.rake > rake jasmine:ci https://github.com/pivotal/jasmine-gemThursday, 19 July 12
    • Maven > mvn clean test http://searls.github.com/jasmine-maven-plugin/Thursday, 19 July 12
    • Node.js > jasmine-node specs/ https://github.com/mhevery/jasmine-nodeThursday, 19 July 12
    • 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
    • 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.htmlThursday, 19 July 12
    • 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
    • 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
    • Extending Jasmine With Custom Matchers beforeEach(function() { this.addMatchers({ toHaveLatitude: function(lat) { return this.actual.latitude === lat; }, toHaveLongitude: function(lat) { return this.actual.latitude === lat; }, toHaveLatLongCoordinates: function(lat, lng) { return (this.actual.latitude === lat && this.actual.longitude === lng); } }); });Thursday, 19 July 12
    • 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
    • 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 maintainThursday, 19 July 12
    • 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 browsersThursday, 19 July 12
    • With thanks to our sponsorsThursday, 19 July 12
    • Please complete your feedback forms, and return them to the registration desk for a chance to win a Nokia LumiaThursday, 19 July 12
    • QuestionsThursday, 19 July 12