understanding javascript testing

55
JavaScript Testing John Resig http://ejohn.org / - http://twitter.com/jeresig

Upload: jeresig

Post on 08-Sep-2014

48.143 views

Category:

Technology


1 download

DESCRIPTION

A talk given at the Boston Developer Day and at the 2009 jQuery Conference.

TRANSCRIPT

Page 1: Understanding JavaScript Testing

JavaScript TestingJohn Resig

http://ejohn.org/ - http://twitter.com/jeresig

Page 2: Understanding JavaScript Testing

Why Test JavaScript?✦ Cross-browser issues.✦ The possibility for causing an unforeseen

problem is simply too great.

Page 3: Understanding JavaScript Testing

What should I use?

Page 4: Understanding JavaScript Testing

Looong Tail

0

62.5

125.0

187.5

250.0

Other

Page 5: Understanding JavaScript Testing

People don’t test. :-(

0

225

450

675

900

None

Page 6: Understanding JavaScript Testing

Basic Components✦ Writing and understanding a JavaScript

test suite is easy.✦ Test Suite

✦ Tests✦ Assertions

✦ Async Tests✦ Test Runner

Page 7: Understanding JavaScript Testing

Assertions <html>  <head>    <title>Test Suite</title>    <script>    function assert( value, desc ) {      var li = document.createElement("li");      li.className = value ? "pass" : "fail";      li.appendChild( document.createTextNode( desc ) );      document.getElementById("results").appendChild( li );    } 

       window.onload = function(){      assert( true, "The test suite is running." );    };    </script>    <style>      #results li.pass { color: green; }      #results li.fail { color: red; }    </style>  </head>  <body>    <ul id="results"></ul>  </body>  </html> 

Page 8: Understanding JavaScript Testing

Tests     test("A test.", function(){        assert( true, "First assertion completed" );        assert( true, "Second assertion completed" );        assert( true, "Third assertion completed" );      }); 

           test("Another test.", function(){        assert( true, "First test completed" );        assert( false, "Second test failed" );        assert( true, "Third assertion completed" );      }); 

Page 9: Understanding JavaScript Testing

Tests   var results; 

       function assert( value, desc ) {      var li = document.createElement("li");      li.className = value ? "pass" : "fail";      li.appendChild( document.createTextNode( desc ) );      results.appendChild( li );      if ( !value ) {        li.parentNode.parentNode.className = "fail";      }      return li;    } 

       function test(name, fn){      results = document.getElementById("results");      results = assert( true, name ).appendChild(        document.createElement("ul") );      fn();    } 

Page 10: Understanding JavaScript Testing

Async Tests test(function(){    pause();    setTimeout(function(){      assert( true, "First test completed" );      resume();    }, 100);  }); 

   test(function(){    pause();    setTimeout(function(){      assert( true, "Second test completed" );      resume();    }, 200);  }); 

Page 11: Understanding JavaScript Testing

Async Tests (function(){    var queue = [], paused = false; 

       this.test = function(fn){      queue.push( fn );      runTest();    }; 

       this.pause = function(){      paused = true;    }; 

       this.resume = function(){      paused = false;      setTimeout(runTest, 1);    }; 

       function runTest(){      if ( !paused && queue.length ) {        queue.shift()();        if ( !paused ) {          resume();        }      }    }  })(); 

Page 12: Understanding JavaScript Testing

TestRunner✦ Responsible for loading an executing tests.✦ Sometimes individual test groups can run

standalone (Dojo) sometimes they require the test runner (QUnit, JSUnit).

Page 13: Understanding JavaScript Testing

Popular Test Frameworks

0

62.5

125.0

187.5

250.0

QUnit JSUnit Selenium YUITest FireUnit Screw.Unit JSSpec

Page 14: Understanding JavaScript Testing

Unit Testing✦ Break code into logical chucks for testing.✦ Focus on one method at a time.✦ Popular Frameworks:

✦ QUnit✦ JSUnit✦ YUITest

Page 15: Understanding JavaScript Testing

QUnit✦ Unit Testing framework built for jQuery.✦ Additional features:

✦ Supports asynchronous testing.✦ Can break code into modules.✦ Supports test timeouts.

✦ http://docs.jquery.com/QUnit

Page 16: Understanding JavaScript Testing

QUnit Style test("a basic test example", function() {    ok( true, "this test is fine" );    var value = "hello";    equals( "hello", value, "We expect value to be hello" );  }); 

   module("Module A"); 

   test("first test within module", function() {    ok( true, "all pass" );  }); 

   test("second test within module", function() {    ok( true, "all pass" );  }); 

   module("Module B"); 

   test("some other test", function() {    expect(2);    equals( true, false, "failing test" );    equals( true, true, "passing test" );  }); 

Page 17: Understanding JavaScript Testing

QUnit

Page 18: Understanding JavaScript Testing

JSUnit✦ One of the oldest JavaScript testing

frameworks.✦ A port of JUnit to JavaScript, circa 2001.✦ Code feels very 2001 (frames!)✦ http://www.jsunit.net/

Page 19: Understanding JavaScript Testing

JSUnit       function coreSuite() {            var result = new top.jsUnitTestSuite();            result.addTestPage("tests/jsUnitAssertionTests.html");            result.addTestPage("tests/jsUnitSetUpTearDownTests.html");            result.addTestPage("tests/jsUnitRestoredHTMLDivTests.html");            result.addTestPage("tests/jsUnitFrameworkUtilityTests.html");            result.addTestPage("tests/jsUnitOnLoadTests.html");            result.addTestPage("tests/jsUnitUtilityTests.html");            return result;        } 

               function serverSuite() {            var result = new top.jsUnitTestSuite();            result.addTestPage("tests/server/jsUnitVersionCheckTests.html");            result.addTestPage("tests/server/jsUnitServerAjaxTests.html");            return result;        } 

               function librariesSuite() {            var result = new top.jsUnitTestSuite();            result.addTestPage("tests/jsUnitMockTimeoutTest.html");            return result;        } 

               function suite() {            var newsuite = new top.jsUnitTestSuite();            newsuite.addTestSuite(coreSuite());            newsuite.addTestSuite(serverSuite());            newsuite.addTestSuite(librariesSuite());            return newsuite;        }

Page 20: Understanding JavaScript Testing

JSUnit function testAssertNotUndefined() {      assertNotUndefined("1 should not be undefined", 1);      assertNotUndefined(1);  } 

   function testAssertNaN() {      assertNaN("a string should not be a number", "string");      assertNaN("string");  } 

   function testAssertNotNaN() {      assertNotNaN("1 should not be not a number", 1);      assertNotNaN(1);  } 

   function testFail() {      var excep = null;      try {          fail("Failure message");      } catch (e) {          excep = e;      }      assertJsUnitException("fail(string) should throw a JsUnitException", excep);  } 

   function testTooFewArguments() {      var excep = null;      try {          assert();      } catch (e1) {          excep = e1;      }      assertNonJsUnitException("Calling an assertion function with too \ few arguments should throw an exception", excep);  }

Page 21: Understanding JavaScript Testing

JSUnit

Page 22: Understanding JavaScript Testing

YUITest (2 & 3)✦ Testing framework built and developed by

Yahoo (released Oct 2008).✦ Completely overhauled to go with YUI v3.✦ Features:

✦ Supports async tests.✦ Has good event simulation.

✦ v2: http://developer.yahoo.com/yui/examples/yuitest/

✦ v3: http://developer.yahoo.com/yui/3/test/

Page 23: Understanding JavaScript Testing

YUITest 2  YAHOO.example.yuitest.ArrayTestCase = new YAHOO.tool.TestCase({      name : "Array Tests", 

           setUp : function () {          this.data = [0,1,2,3,4]      }, 

           tearDown : function () {          delete this.data;      }, 

             testPop : function () {          var Assert = YAHOO.util.Assert;                   var value = this.data.pop(); 

                   Assert.areEqual(4, this.data.length);          Assert.areEqual(4, value);                  },         

           testPush : function () {          var Assert = YAHOO.util.Assert; 

                   this.data.push(5); 

                   Assert.areEqual(6, this.data.length);          Assert.areEqual(5, this.data[5]);                  } }); 

Page 24: Understanding JavaScript Testing

YUITest 2

Page 25: Understanding JavaScript Testing

YUITest 3 Y.example.test.DataTestCase = new Y.Test.Case({      name : "Data Tests", 

           setUp : function () {          this.data = {              name: "test",              year: 2007,              beta: true          };      },            tearDown : function () {          delete this.data;      }, 

           testName : function () {          var Assert = Y.Assert; 

                   Assert.isObject(this.data);          Assert.isString(this.data.name);          Assert.areEqual("test", this.data.name);                  }, 

           testYear : function () {          var Assert = Y.Assert; 

                   Assert.isObject(this.data);          Assert.isNumber(this.data.year);          Assert.areEqual(2007, this.data.year);                  } }); 

Page 26: Understanding JavaScript Testing

YUITest 3

Page 27: Understanding JavaScript Testing

Behavior Testing✦ Similar to unit testing, but broken up by

task.✦ Functionally very similar to unit testing,

uses different terminology✦ Popular frameworks:

✦ Screw.Unit✦ JSSpec

Page 29: Understanding JavaScript Testing

Screw.Unit describe("a nested describe", function() {    var invocations = []; 

     before(function() {      invocations.push("before");    }); 

     describe("a doubly nested describe", function() {      before(function() {        invocations.push('inner before');      }); 

       it("runs befores in all ancestors prior to an it", function() {        expect(invocations).to(equal, ["before", "inner before"]);      });    });  }); 

Page 30: Understanding JavaScript Testing

Screw.Unit

Page 31: Understanding JavaScript Testing

JSSpec✦ Used by MooTools as their testing

framework.✦ http://jania.pe.kr/aw/moin.cgi/JSSpec

Page 32: Understanding JavaScript Testing

JSSpec describe('"Should have"s', {    'String length': function() {      expect("Hello").should_have(4, "characters");    },    'Array length': function() {      expect([1,2,3]).should_have(4, "items");    },    'Object\'s item length': function() {      expect({name:'Alan Kang', email:'[email protected]', accounts:['A', 'B']}).should_have(3, "accounts");    },    'No match': function() {      expect("This is a string").should_have(5, "players");    },    'Exactly': function() {      expect([1,2,3]).should_have_exactly(2, "items");    },    'At least': function() {      expect([1,2,3]).should_have_at_least(4, "items");    },    'At most': function() {      expect([1,2,3]).should_have_at_most(2, "items");    }  }) 

Page 33: Understanding JavaScript Testing

JSSpec

Page 34: Understanding JavaScript Testing

Automation✦ Functional Testing✦ Browser launching✦ Server-Side

Page 35: Understanding JavaScript Testing

Functional Testing✦ Selenium IDE✦ There may be others by Selenium is far

and away the best.

Page 36: Understanding JavaScript Testing

Selenium IDE✦ Records and automates actions performed

by a user.✦ An extension for Firefox that records the

actions.✦ Can play them back in all browsers (limited by cross-domain issues).

✦ Primarily for testing web applications - everyone should use it, though!

✦ http://seleniumhq.org/projects/ide/

Page 37: Understanding JavaScript Testing

Selenium IDE

Page 38: Understanding JavaScript Testing

Browser Launching✦ Automates the process of opening browser

windows, running tests, and getting results.✦ Frequently require a specific framework.✦ Popular frameworks:

✦ WebDriver http://code.google.com/p/webdriver/ (Java)

✦ Waitr http://wtr.rubyforge.org/ (Ruby)✦ JsTestDriver http://code.google.com/p/

js-test-driver/ (Java)✦ Selenium RC http://seleniumhq.org/

projects/remote-control/ (Java)

Page 39: Understanding JavaScript Testing

Browser Launching

Page 40: Understanding JavaScript Testing

Server-Side✦ Ignore the browser! Simulate it on the

server-side.✦ Almost always uses Java + Rhino to

construct a browser.✦ Some frameworks:

✦ Crosscheck✦ Env.js✦ Blueridge

Page 41: Understanding JavaScript Testing

Server-Side✦ Crosscheck

✦ Pure Java, even simulates browser bugs.✦ http://www.thefrontside.net/crosscheck

✦ Env.js✦ Pure JavaScript, focuses on standards

support.✦ http://github.com/thatcher/env-js/tree/

master✦ Blueridge

✦ Env.js + Screw.Unit + Rhino✦ http://github.com/relevance/blue-ridge/

Page 42: Understanding JavaScript Testing

Env.js$ java -jar build/js.jarRhino 1.6 release 6 2007 06 28js> load('build/runtest/env.js');

js> window.location = 'test/index.html';test/index.html

js> load('dist/jquery.js');

// Add pretty printing to jQuery objects:js> jQuery.fn.toString = DOMNodeList.prototype.toString;

js> $('span').remove();[ <span#å°åŒ—Taibei>, <span#å°åŒ—>, <span#utf8class1>, <span#utf8class2>, <span#foo:bar>, <span#test.foo[5]bar> ]

// Yes - UTF-8 is supported in DOM documents!js> $('span')[ ]

js> $('div').append('<span><b>hello!</b> world</span>');[ <div#main>, <div#foo> ]

js> $('span')[ <span>, <span> ]

js> $('span').text()hello! worldhello! world

Page 43: Understanding JavaScript Testing

Distributed✦ Selenium Grid

✦ Push Selenium tests out to many machines (that you manage), simultaneously.

✦ Collect and store the results.✦ http://selenium-grid.seleniumhq.org/

✦ TestSwarm✦ Push tests to a distributed swarm of

clients.✦ Results viewable on the server.✦ http://testswarm.com/

Page 44: Understanding JavaScript Testing

TestSwarm

Page 45: Understanding JavaScript Testing

Choose Your Browsers

Page 46: Understanding JavaScript Testing

Cost / Benefit

IE 7 IE 6 FF 3 Safari 3 Opera 9.5

Cost Benefit

Draw a line in the sand.

Page 47: Understanding JavaScript Testing

Graded Support

Yahoo Browser Compatibility

Page 48: Understanding JavaScript Testing

Browser Support GridIE Firefox Safari Opera Chrome

Previous 6.0 2.0 3.0 9.5

Current 7.0 3.0 3.2 9.6 1.0

Next 8.0 3.1 4.0 10.0 2.0

jQuery Browser Support

Page 49: Understanding JavaScript Testing

Browser Support GridIE Firefox Safari Opera Chrome

Previous 6.0 2.0 3.0 9.5

Current 7.0 3.0 3.2 9.6 1.0

Next 8.0 3.1 4.0 10.0 2.0

jQuery 1.3 Browser Support

Page 50: Understanding JavaScript Testing

The Scaling Problem✦ The Problem:

✦ jQuery has 6 test suites✦ Run in 11 browsers✦ (Not even including multiple platforms!)

✦ All need to be run for every commit, patch, and plugin.

✦ JavaScript testing doesn’t scale well.

Page 51: Understanding JavaScript Testing

Distributed Testing✦ Hub server✦ Clients connect and help run tests✦ A simple JavaScript client that can be run

in all browsers✦ Including mobile browsers!

✦TestSwarm

Page 52: Understanding JavaScript Testing

TestSwarm

FF 3

FF 3

FF 3.5 FF 3.5 FF 3.5IE 6

IE 6

IE 6

IE 7

IE 7

Op 9

Test Suite Test Suite Test Suite

Page 53: Understanding JavaScript Testing

Manual Testing✦ Push tests to users who follow pre-defined

steps✦ Answer ‘Yes’/’No’ questions which are

pushed back to the server.✦ An effective way to distribute manual test

load to dozens of clients.

Page 54: Understanding JavaScript Testing

TestSwarm.com✦ Incentives for top testers (t-shirts, books)✦ Will be opening for alpha testing very soon✦ Help your favorite JavaScript library

become better tested!✦ http://testswarm.com