SlideShare una empresa de Scribd logo
1 de 38
Descargar para leer sin conexión
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
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
Thursday, 19 July 12

Más contenido relacionado

La actualidad más candente

NetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportNetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience Report
Anton Arhipov
 
CodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical GroovyCodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical Groovy
Codecamp Romania
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Thomas Fuchs
 

La actualidad más candente (20)

Let the type system be your friend
Let the type system be your friendLet the type system be your friend
Let the type system be your friend
 
NetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportNetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience Report
 
Google Guava - Core libraries for Java & Android
Google Guava - Core libraries for Java & AndroidGoogle Guava - Core libraries for Java & Android
Google Guava - Core libraries for Java & Android
 
Scala in practice
Scala in practiceScala in practice
Scala in practice
 
CodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical GroovyCodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical Groovy
 
Backbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The BrowserBackbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The Browser
 
jQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyjQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journey
 
OSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
OSDC.fr 2012 :: Cascalog : progammation logique pour HadoopOSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
OSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
 
Clean code with google guava jee conf
Clean code with google guava jee confClean code with google guava jee conf
Clean code with google guava jee conf
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com Jetpack
 
History of jQuery
History of jQueryHistory of jQuery
History of jQuery
 
Google guava
Google guavaGoogle guava
Google guava
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
JavaScript & HTML5 - Brave New World
JavaScript & HTML5 - Brave New WorldJavaScript & HTML5 - Brave New World
JavaScript & HTML5 - Brave New World
 
BVJS
BVJSBVJS
BVJS
 
Easy Button
Easy ButtonEasy Button
Easy Button
 
Google Guava
Google GuavaGoogle Guava
Google Guava
 
Object oriented javascript
Object oriented javascriptObject oriented javascript
Object oriented javascript
 
Object Oriented JavaScript
Object Oriented JavaScriptObject Oriented JavaScript
Object Oriented JavaScript
 
Google guava overview
Google guava overviewGoogle guava overview
Google guava overview
 

Similar a Testing javascriptwithjasmine sydjs

Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA
Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPAIntegrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA
Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA
Cheng Ta Yeh
 
Declarative web data visualization using ClojureScript
Declarative web data visualization using ClojureScriptDeclarative web data visualization using ClojureScript
Declarative web data visualization using ClojureScript
OSCON Byrum
 
mDevCamp - The Best from Google IO
mDevCamp - The Best from Google IOmDevCamp - The Best from Google IO
mDevCamp - The Best from Google IO
ondraz
 
Get started with YUI
Get started with YUIGet started with YUI
Get started with YUI
Adam Lu
 

Similar a Testing javascriptwithjasmine sydjs (20)

Understanding JavaScript
Understanding JavaScriptUnderstanding JavaScript
Understanding JavaScript
 
Js in the open
Js in the openJs in the open
Js in the open
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
 
Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA
Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPAIntegrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA
Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA
 
Declarative web data visualization using ClojureScript
Declarative web data visualization using ClojureScriptDeclarative web data visualization using ClojureScript
Declarative web data visualization using ClojureScript
 
The next step, part 2
The next step, part 2The next step, part 2
The next step, part 2
 
mDevCamp - The Best from Google IO
mDevCamp - The Best from Google IOmDevCamp - The Best from Google IO
mDevCamp - The Best from Google IO
 
Sane Async Patterns
Sane Async PatternsSane Async Patterns
Sane Async Patterns
 
Organizing Code with JavascriptMVC
Organizing Code with JavascriptMVCOrganizing Code with JavascriptMVC
Organizing Code with JavascriptMVC
 
SfCon: Test Driven Development
SfCon: Test Driven DevelopmentSfCon: Test Driven Development
SfCon: Test Driven Development
 
Introduction to jQuery
Introduction to jQueryIntroduction to jQuery
Introduction to jQuery
 
Get started with YUI
Get started with YUIGet started with YUI
Get started with YUI
 
Introduccion a Jasmin
Introduccion a JasminIntroduccion a Jasmin
Introduccion a Jasmin
 
06 jQuery #burningkeyboards
06 jQuery  #burningkeyboards06 jQuery  #burningkeyboards
06 jQuery #burningkeyboards
 
Student management system
Student management systemStudent management system
Student management system
 
DOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQuery
 
662305 11
662305 11662305 11
662305 11
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 
Ian 20150116 java script oop
Ian 20150116 java script oopIan 20150116 java script oop
Ian 20150116 java script oop
 

Último

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 

Último (20)

Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 

Testing javascriptwithjasmine sydjs

  • 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. Is JavaScript Ever Really That Simple? Thursday, 19 July 12
  • 5. What About ... • Asynchronous goodness • Interacting with teh DOMz • Evil Legacy Code • Continuous Integration • Clean readable tests that reflect your domain Thursday, 19 July 12
  • 6. Approaches To Testing Asynchronous Code Thursday, 19 July 12
  • 7. Let’s Load Some JSON [ { "firstName": "Jo", "lastName": "Cranford", "company": "Atlassian" }, { "firstName": "Rachel", "lastName": "Laycock", "company": "ThoughtWorks" } ] Thursday, 19 July 12
  • 8. The JavaScript Code var Presentation = function() { this.presenters = []; }; Presentation.prototype.loadPresenters = function() { var presenters = this.presenters; $.getJSON("people.json", function(data) { $.each(data, function(idx, person) { presenters.push(person); }); }); }; Thursday, 19 July 12
  • 9. Easy, Right? describe("How not to test an asynchronous function", function () { it("should load the presenters", function () { var presentation = new Presentation(); presentation.loadPresenters(); expect(presentation.presenters.length).toBe(2); }); }); Thursday, 19 July 12
  • 10. Well ... Not So Much. Thursday, 19 July 12
  • 11. But This Might Work ... describe("Still not ideal though", function () { it("should load the presenters", function () { spyOn($, "getJSON").andCallFake(function (url, callback) { callback([{},{}]); }) var presentation = new Presentation(); presentation.loadPresenters(); expect(presentation.presenters.length).toBe(2); }); }); Thursday, 19 July 12
  • 12. A Little Detour ... Thursday, 19 July 12
  • 13. Spy On An Existing Method it("can spy on an existing method", function() { var fakeElement = $("<div style='display:none'></div>"); spyOn(fakeElement, 'show'); var toggleable = new Toggleable(fakeElement); toggleable.toggle(); expect(fakeElement.show).toHaveBeenCalled(); }); Thursday, 19 July 12
  • 14. Spy On An Existing Method it("can create a method for you", function() { var fakeElement = {}; fakeElement.css = function() {}; fakeElement.show = jasmine.createSpy("Show spy"); var toggleable = new Toggleable(fakeElement); toggleable.toggle(); expect(fakeElement.show).toHaveBeenCalled(); }); Thursday, 19 July 12
  • 15. Wait, There’s More ... • expect(spy).not.toHaveBeenCalled() • createSpy().andReturn(something) • createSpy().andCallFake(function() {}) • createSpy().andCallThrough() Thursday, 19 July 12
  • 16. Spy On The Details • expect(spy).toHaveBeenCalled() • expect(spy.callCount).toBe(x) • expect(spy).toHaveBeenCalledWith() • Tip: use jasmine.any(Function/Object) for parameters you don’t care about Thursday, 19 July 12
  • 17. ... And We’re Back. Sooooo ... spies are great and all, but what if your callback function takes a while to run? Thursday, 19 July 12
  • 18. Don’t Do This At Home. Presentation.prototype.loadPresentersMoreSlowly = function() { var preso = this; $.getJSON("people.json", function(data) { setTimeout(function() { $.each(data, function(idx, person) { preso.presenters.push(person); }); }, 2000); }); }; Thursday, 19 July 12
  • 19. Don’t Do This, Either. it("should have loaded after three seconds, right?", function() { spyOn($, "getJSON").andCallFake(function(url, callback) { callback([{}, {}]); }) var presentation = new Presentation(); presentation.loadPresentersMoreSlowly(); setTimeout(function() { expect(presentation.presenters.length).toBe(2); }, 3000); }); Thursday, 19 July 12
  • 20. But What If I Just ... Presentation.prototype.loadPresentersMoreSlowly = function() { var preso = this; $.getJSON("people.json", function(data) { setTimeout(function() { $.each(data, function(idx, person) { preso.presenters.push(person); }); preso.presentersHaveLoaded = true; }, 2000); }); }; Thursday, 19 July 12
  • 21. Now Wait, Wait ... RUN! it("should load the presenters", function() { spyOn($, "getJSON").andCallFake(function(url, callback) { callback([{}, {}]); }) var presentation = new Presentation(); presentation.loadPresentersMoreSlowly(); waitsFor(function() { return presentation.presentersHaveLoaded; }, "presenters have loaded"); runs(function() { expect(presentation.presenters.length).toBe(2); }); }); Thursday, 19 July 12
  • 22. Testing Interaction With The DOM • Do you REALLY need to? • Tests will have a high maintenance cost • Instead separate logic from view and test logic • Use templates for the view Thursday, 19 July 12
  • 23. Testing Interaction With The DOM it("should display the score", function() { setFixtures("<div id='score'></div>"); var bowlingGameView = new BowlingGameView(); bowlingGameView.showScore(100); expect($("#score").text()).toBe("Your current score is 100"); }); https://github.com/velesin/jasmine-jquery Thursday, 19 July 12
  • 24. Legacy (untested) JavaScript Code • Long methods • Violation of Single Responsibility Principle • Side effects • Lack of dependency injection • Lots of new X() • Unclear intentions Thursday, 19 July 12
  • 25. Testing Interaction it("should call the method on the dependency", function() { var dependency = {}; dependency.method = jasmine.createSpy(); var myObject = new Something(dependency); myObject.doSomething(); expect(dependency.method).toHaveBeenCalled(); }); Thursday, 19 July 12
  • 26. If Dependencies Aren’t Injected ... var LegacySomething = function() { this.doSomething = function() { var dependency = new Dependency(); dependency.method(); }; }; Thursday, 19 July 12
  • 27. Create Stubs it("is a pain but not impossible", function() { Dependency = function() {}; Dependency.prototype.method = jasmine.createSpy() var myObject = new LegacySomething(); myObject.doSomething(); expect(Dependency.prototype.method).toHaveBeenCalled(); }); Thursday, 19 July 12
  • 28. Continuous Integration • Ruby Gem • Maven • Node.js • Rhino (Java) Thursday, 19 July 12
  • 29. Ruby Gem require 'jasmine' load 'jasmine/tasks/jasmine.rake' > rake jasmine:ci https://github.com/pivotal/jasmine-gem Thursday, 19 July 12
  • 30. Maven > mvn clean test http://searls.github.com/jasmine-maven-plugin/ Thursday, 19 July 12
  • 31. Node.js > jasmine-node specs/ https://github.com/mhevery/jasmine-node Thursday, 19 July 12
  • 32. Rhino • Download: • Rhino (js.jar) from Mozilla • env.rhino.js from www.envjs.com • Jasmine console reporter from Larry Myers Jasmine Reporters project (github) http://www.build-doctor.com/2010/12/08/javascript-bdd-jasmine/ Thursday, 19 July 12
  • 33. Rhino load('env.rhino.1.2.js'); Envjs.scriptTypes['text/javascript'] = true; var specFile; for (i = 0; i < arguments.length; i++) { specFile = arguments[i]; console.log("Loading: " + specFile); window.location = specFile } > java -jar js.jar -opt -1 env.bootstrap.js ../SpecRunner.html Thursday, 19 July 12
  • 34. Extending Jasmine With Custom Matchers it("should match the latitude and longitude", function() { var pointOnMap = { latitude: "51.23", longitude: "-10.14" }; expect(pointOnMap.latitude).toBe("51.23"); expect(pointOnMap.longitude).toBe("-10.14"); }); it("should match the latitude and longitude", function() { var pointOnMap = { latitude: "51.23", longitude: "-10.14" }; expect(pointOnMap).toHaveLatitude("51.23"); expect(pointOnMap).toHaveLongitude("-10.14"); }); Thursday, 19 July 12
  • 35. Extending Jasmine With Custom Matchers it("should match the latitude and longitude", function() { var pointOnMap = { latitude: "51.23", longitude: "-10.14" }; expect(pointOnMap).toHaveLatLongCoordinates("51.23", "-10.14"); }); Thursday, 19 July 12
  • 36. 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
  • 37. 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