SlideShare a Scribd company logo
1 of 57
Download to read offline
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

What's hot

Promise: async programming hero
Promise: async programming heroPromise: async programming hero
Promise: async programming heroThe Software House
 
The Ring programming language version 1.7 book - Part 73 of 196
The Ring programming language version 1.7 book - Part 73 of 196The Ring programming language version 1.7 book - Part 73 of 196
The Ring programming language version 1.7 book - Part 73 of 196Mahmoud Samir Fayed
 
Persisting Data on SQLite using Room
Persisting Data on SQLite using RoomPersisting Data on SQLite using Room
Persisting Data on SQLite using RoomNelson Glauber Leal
 
Ciconf 2012 - Better than Ad-hoc
Ciconf 2012 - Better than Ad-hocCiconf 2012 - Better than Ad-hoc
Ciconf 2012 - Better than Ad-hocCalvin Froedge
 
Scala meetup
Scala meetupScala meetup
Scala meetup扬 明
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackNelson Glauber Leal
 
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)Simon Su
 
History of jQuery
History of jQueryHistory of jQuery
History of jQueryjeresig
 
Object oriented javascript
Object oriented javascriptObject oriented javascript
Object oriented javascriptShah Jalal
 
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...Stephen Chin
 
Easy undo.key
Easy undo.keyEasy undo.key
Easy undo.keyzachwaugh
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonfRafael Dohms
 
Snapshot Testing @ CocoaheadsNL
Snapshot Testing @ CocoaheadsNLSnapshot Testing @ CocoaheadsNL
Snapshot Testing @ CocoaheadsNLLars Lockefeer
 
code for quiz in my sql
code for quiz  in my sql code for quiz  in my sql
code for quiz in my sql JOYITAKUNDU1
 

What's hot (20)

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

Recently uploaded

Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 

Recently uploaded (20)

Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 

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