SlideShare a Scribd company logo
Testing JavaScript
          LIKE A BOSS
                       Jo Cranford
                       @jocranford




Thursday, 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 Shizzle




Thursday, 19 July 12
Jasmine In Your Project


    •   Ruby Gem

    •   Maven

    •   Node.js

    •   Standalone



Thursday, 19 July 12
Ruby Gem




                              > jasmine init
                              > rake jasmine




                       https://github.com/pivotal/jasmine-gem
Thursday, 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-node
Thursday, 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/downloads
Thursday, 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 domain


Thursday, 19 July 12
Approaches To Testing
                        Asynchronous Code




Thursday, 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 about


Thursday, 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 view



Thursday, 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-jquery
Thursday, 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 intentions

Thursday, 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-gem
Thursday, 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-node
Thursday, 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.html



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.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 maintain
Thursday, 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 browsers




Thursday, 19 July 12
With thanks to our sponsors




Thursday, 19 July 12
Please complete your feedback
           forms, and return them to the
          registration desk for a chance
                                to win a
                           Nokia Lumia




Thursday, 19 July 12
Questions




Thursday, 19 July 12

More Related Content

PDF
Testing javascriptwithjasmine sydjs
PPTX
Less ismorewithcoffeescript webdirectionsfeb2012
PDF
Using Templates to Achieve Awesomer Architecture
PDF
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion
PDF
Programa simulacion de ventas de aeropuerto
PDF
Functionality Focused Code Organization
PPTX
NetBeans Plugin Development: JRebel Experience Report
PDF
Let the type system be your friend
Testing javascriptwithjasmine sydjs
Less ismorewithcoffeescript webdirectionsfeb2012
Using Templates to Achieve Awesomer Architecture
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion
Programa simulacion de ventas de aeropuerto
Functionality Focused Code Organization
NetBeans Plugin Development: JRebel Experience Report
Let the type system be your friend

What's hot (20)

PDF
Promise: async programming hero
PDF
The Ring programming language version 1.7 book - Part 73 of 196
PDF
Persisting Data on SQLite using Room
RTF
Easy Button
PDF
Ciconf 2012 - Better than Ad-hoc
PPTX
Scala meetup
PDF
Arquitetando seu aplicativo Android com Jetpack
KEY
Backbone.js
PDF
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
PDF
History of jQuery
PPTX
Object oriented javascript
PDF
Scala in practice
PPTX
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
ZIP
Easy undo.key
PDF
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
PDF
Python speleology
PPTX
PDF
Snapshot Testing @ CocoaheadsNL
DOC
code for quiz in my sql
Promise: async programming hero
The Ring programming language version 1.7 book - Part 73 of 196
Persisting Data on SQLite using Room
Easy Button
Ciconf 2012 - Better than Ad-hoc
Scala meetup
Arquitetando seu aplicativo Android com Jetpack
Backbone.js
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
History of jQuery
Object oriented javascript
Scala in practice
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
Easy undo.key
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
Python speleology
Snapshot Testing @ CocoaheadsNL
code for quiz in my sql
Ad

Similar to Testing javascriptwithjasmine ddd-sydney (20)

PDF
JavaScript Primer
PDF
Michael mahlberg exploratory-testing-the_missing_half_of_bdd
PPTX
Java Script Isn\'t a Toy Anymore
PPTX
Tests in Javascript using Jasmine and Testacular
PPT
High-Quality JavaScript Code
PDF
Building Rich User Experiences Without JavaScript Spaghetti
KEY
Test Driven Development - For Girl Geeks Night Sydney
PDF
Teaching and Learning TDD in the Coding Dojo
PDF
03 Advanced JavaScript
PDF
JavaScript 101
PDF
Enterprise javascriptsession3
PPTX
Javascript Testing with Jasmine 101
PDF
Intro to JavaScript Testing
PDF
A New Baseline for Front-End Devs
PDF
2013-06-24 - Software Craftsmanship with JavaScript
PDF
2013-06-15 - Software Craftsmanship mit JavaScript
PDF
Testing outside of the Ruby World
PDF
test
PDF
test
PDF
Jasmine - why JS tests don't smell fishy
JavaScript Primer
Michael mahlberg exploratory-testing-the_missing_half_of_bdd
Java Script Isn\'t a Toy Anymore
Tests in Javascript using Jasmine and Testacular
High-Quality JavaScript Code
Building Rich User Experiences Without JavaScript Spaghetti
Test Driven Development - For Girl Geeks Night Sydney
Teaching and Learning TDD in the Coding Dojo
03 Advanced JavaScript
JavaScript 101
Enterprise javascriptsession3
Javascript Testing with Jasmine 101
Intro to JavaScript Testing
A New Baseline for Front-End Devs
2013-06-24 - Software Craftsmanship with JavaScript
2013-06-15 - Software Craftsmanship mit JavaScript
Testing outside of the Ruby World
test
test
Jasmine - why JS tests don't smell fishy
Ad

Recently uploaded (20)

PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPT
Teaching material agriculture food technology
PDF
cuic standard and advanced reporting.pdf
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Empathic Computing: Creating Shared Understanding
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Encapsulation theory and applications.pdf
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
A Presentation on Artificial Intelligence
PPTX
Cloud computing and distributed systems.
PDF
A comparative analysis of optical character recognition models for extracting...
PPTX
Programs and apps: productivity, graphics, security and other tools
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
20250228 LYD VKU AI Blended-Learning.pptx
Teaching material agriculture food technology
cuic standard and advanced reporting.pdf
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Spectral efficient network and resource selection model in 5G networks
Diabetes mellitus diagnosis method based random forest with bat algorithm
Empathic Computing: Creating Shared Understanding
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Dropbox Q2 2025 Financial Results & Investor Presentation
Encapsulation theory and applications.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
A Presentation on Artificial Intelligence
Cloud computing and distributed systems.
A comparative analysis of optical character recognition models for extracting...
Programs and apps: productivity, graphics, security and other tools

Testing javascriptwithjasmine ddd-sydney

  • 1. Testing JavaScript LIKE A BOSS Jo Cranford @jocranford Thursday, 19 July 12
  • 2. Y U NO JAVASCRIPT TESTS? 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
  • 8. Jasmine In Your Project • Ruby Gem • Maven • Node.js • Standalone Thursday, 19 July 12
  • 9. Ruby Gem > jasmine init > rake jasmine https://github.com/pivotal/jasmine-gem 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
  • 11. Node.js > npm install jasmine-node -g > jasmine-node specs/ https://github.com/mhevery/jasmine-node Thursday, 19 July 12
  • 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/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
  • 14. 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
  • 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
  • 19. Is JavaScript Ever Really That Simple? Thursday, 19 July 12
  • 20. What About ... • Asynchronous goodness • Interacting with teh DOMz • Evil Legacy Code • Continuous Integration • Clean readable tests that reflect your domain Thursday, 19 July 12
  • 21. Approaches To Testing Asynchronous Code Thursday, 19 July 12
  • 22. Let’s Load Some JSON [ { "firstName": "Jo", "lastName": "Cranford", "company": "Atlassian" }, { "firstName": "Rachel", "lastName": "Laycock", "company": "ThoughtWorks" } ] 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
  • 25. Well ... Not So Much. 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
  • 27. A Little Detour ... 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
  • 43. Continuous Integration • Ruby Gem • Maven • Node.js • Rhino (Java) Thursday, 19 July 12
  • 44. Ruby Gem require 'jasmine' load 'jasmine/tasks/jasmine.rake' > rake jasmine:ci https://github.com/pivotal/jasmine-gem 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
  • 51. 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
  • 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
  • 55. With thanks to our sponsors Thursday, 19 July 12
  • 56. Please complete your feedback forms, and return them to the registration desk for a chance to win a Nokia Lumia Thursday, 19 July 12