writing and testing javascript-heavy web 2.0 apps with jsunit
DESCRIPTION
With the advent of the so-called Web 2.0 platform, more and more applications are using client-side JavaScript for vital features. In fact, some applications are so JS-heavy that they redefine JavaScript as a full-fledged application development language. In this tutorial we discuss some architectural considerations of JS- and AJAX-heavy applications and present in detail our testing framework, with plenty of code examples.TRANSCRIPT
Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit
Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit
Authors:Alex Chaffee, Pivotal Labs
Edward Hieatt, Pivotal Labs
Authors:Alex Chaffee, Pivotal Labs
Edward Hieatt, Pivotal Labs
AbstractAbstract
With the advent of the so-called Web 2.0 platform, more and more applications are using client-side
JavaScript for vital features. In fact, some applications are so JS-heavy that they redefine JavaScript as a full-
fledged application development language. In this tutorial we discuss some architectural considerations
of JS- and AJAX-heavy applications and present in detail our testing framework, with plenty of code
examples and live demos.
With the advent of the so-called Web 2.0 platform, more and more applications are using client-side
JavaScript for vital features. In fact, some applications are so JS-heavy that they redefine JavaScript as a full-
fledged application development language. In this tutorial we discuss some architectural considerations
of JS- and AJAX-heavy applications and present in detail our testing framework, with plenty of code
examples and live demos.
JS Application ArchitecturesJS Application Architectures
Traditional: Minimal JS JS for validation JS for user responsiveness JS for DHTML JS for layout tweaks (where CSS falls
down)
Traditional: Minimal JS JS for validation JS for user responsiveness JS for DHTML JS for layout tweaks (where CSS falls
down)
JS Application Architectures (cont.)
JS Application Architectures (cont.)
AJAX Architecture Widgets and functions live on client Use XMLHttpRequest for immediate
or background requests to server Use JSON or XML for data transport
AJAX Architecture Widgets and functions live on client Use XMLHttpRequest for immediate
or background requests to server Use JSON or XML for data transport
JS Application Architectures (cont.)
JS Application Architectures (cont.)
Dynamic Architecture: server-generated JS RJS Google Web Toolkit Big advantage:
unified codebase -> DRY Disadvantages:
Latency Harder to test
Dynamic Architecture: server-generated JS RJS Google Web Toolkit Big advantage:
unified codebase -> DRY Disadvantages:
Latency Harder to test
Application Architectures (cont.)
Application Architectures (cont.)
Client-Server
JS MVC: Heavy JS In this architecture, the JavaScript code
takes the form of a full-fledged application. Using JSON to transfer data back and forth between the server-side API, the JavaScript application maintains its own domain objects and executes its own business rules.
Can violate DRY
Client-Server
JS MVC: Heavy JS In this architecture, the JavaScript code
takes the form of a full-fledged application. Using JSON to transfer data back and forth between the server-side API, the JavaScript application maintains its own domain objects and executes its own business rules.
Can violate DRY
Application Architectures (cont.)
Application Architectures (cont.)
Hybrid Naturally, you can mix and match the
above techniques Complicates your coding and testing Can violate DRY
Hybrid Naturally, you can mix and match the
above techniques Complicates your coding and testing Can violate DRY
Testing StrategiesTesting Strategies
Unit Testing: JSUnit
Integration Testing: Selenium WATIR
Unit Testing: JSUnit
Integration Testing: Selenium WATIR
JavaScript EssenceJavaScript Essence
Dynamic Interpreted Prototype-based OO Object = Hash
Everything (even Function) is a Hash Sort of a mutant hybrid of Java and
Ruby
Dynamic Interpreted Prototype-based OO Object = Hash
Everything (even Function) is a Hash Sort of a mutant hybrid of Java and
Ruby
JavaScript ConsJavaScript Cons Functions have loose binding
“this” isn't always correct Workaround: Use bind
(part of prototype.js) Frustrating syntax and
semantics Symbols are globally scoped
by default You must remember
“this” Too easy to make global
variables null vs. undefined vs. 0 All members are public
Functions have loose binding “this” isn't always correct Workaround: Use bind
(part of prototype.js) Frustrating syntax and
semantics Symbols are globally scoped
by default You must remember
“this” Too easy to make global
variables null vs. undefined vs. 0 All members are public
No OO inheritance Several hacks to simulate
inheritance Rely more on composition
Browser differences (esp. DOM API) and bugs
No “include” or “require” We rolled our own
Painful debugging Unreliable stack traces Two different GCs
Memory leaks via DOM Third-party libraries rarely
tested
No OO inheritance Several hacks to simulate
inheritance Rely more on composition
Browser differences (esp. DOM API) and bugs
No “include” or “require” We rolled our own
Painful debugging Unreliable stack traces Two different GCs
Memory leaks via DOM Third-party libraries rarely
tested
JavaScript Tools and Libraries
JavaScript Tools and Libraries
Firefox ExtensionsFirefox Extensions
Web Developer Firebug ROCKS
Web Developer Firebug ROCKS
Development EnvironmentsDevelopment Environments
IDEA Eclipse TextMate ?
IDEA Eclipse TextMate ?
prototype.js Libraryprototype.js Library
bind $ $H, $A, each, etc. extend Ajax.Request Element.hide Other useful stuff
bind $ $H, $A, each, etc. extend Ajax.Request Element.hide Other useful stuff
JS UI librariesJS UI libraries
script.aculo.us Very Good Popular (easy to find help)
Yahoo UI Excellent Drag and drop, animation, calendar,
etc.
script.aculo.us Very Good Popular (easy to find help)
Yahoo UI Excellent Drag and drop, animation, calendar,
etc.
Asset PackagerAsset Packager
Combines multiple JS files into one Also CSS
Reduces Latency Also Steve Conover’s inline merger
looks promising
Combines multiple JS files into one Also CSS
Reduces Latency Also Steve Conover’s inline merger
looks promising
JSUnitJSUnit
“Controlling The Insanity”
Unit Testing is essential for all but the most trivial JavaScript
“Controlling The Insanity”
Unit Testing is essential for all but the most trivial JavaScript
Pivotal AssertionsPivotal Assertions
clock.jsclock.js
Testing utility class we wrote Overrides setTimeout Allows unit testing of time-based
operations without sleeping
Testing utility class we wrote Overrides setTimeout Allows unit testing of time-based
operations without sleeping
ajax.jsajax.js
AjaxUnit Overrides Prototype AJAX classes Stubs out the network interface Allows unit testing of AJAX calls
AjaxUnit Overrides Prototype AJAX classes Stubs out the network interface Allows unit testing of AJAX calls
Mock Objects in JSMock Objects in JS
We don’t know of any real mocking framework for JS
We use stubs, spies, object mothers and mock methods
We don’t know of any real mocking framework for JS
We use stubs, spies, object mothers and mock methods
Client-Server Communication
Client-Server Communication
Client-Server Communication: Let me
count the ways
Client-Server Communication: Let me
count the ways HTTP
Including GET, POST, cookies, etc.
CGI AJAX+XML AJAX+JSON AJAX+HTML AJAX+JS RJS
HTTP Including GET, POST, cookies, etc.
CGI AJAX+XML AJAX+JSON AJAX+HTML AJAX+JS RJS
AJAXAJAX
“Asynchronous JavaScript And XML”
Client calls server with CGI GET or POST via XMLHttpRequest API
Server responds Client does something without
redrawing entire page
“Asynchronous JavaScript And XML”
Client calls server with CGI GET or POST via XMLHttpRequest API
Server responds Client does something without
redrawing entire page
AJAX+XMLAJAX+XML
Response contains XML Original implementation of AJAX Not used much now (?) I've heard of doing XSL but that's
mostly on IE-only apps
Response contains XML Original implementation of AJAX Not used much now (?) I've heard of doing XSL but that's
mostly on IE-only apps
JSONJSON
Evaluates to JS values (hashes and arrays) Example:
{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http: //scd.mmb1.com/image/481989943", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] }}
Evaluates to JS values (hashes and arrays) Example:
{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http: //scd.mmb1.com/image/481989943", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] }}
AJAX+JSON (AJAJ?)AJAX+JSON (AJAJ?)
Proper client-server communication Client sends requests in CGI, gets
responses as pure data Client evaluates data, actively
performs response Interacts with DOM Creates/removes/modifies/copies
HTML elements
Proper client-server communication Client sends requests in CGI, gets
responses as pure data Client evaluates data, actively
performs response Interacts with DOM Creates/removes/modifies/copies
HTML elements
AJAX+HTMLAJAX+HTML
Server executes RHTML/JSP/etc. AJAX response contains HTML Client splats it onto the page
Server executes RHTML/JSP/etc. AJAX response contains HTML Client splats it onto the page
AJAX+JSAJAX+JS
AJAX response contains JavaScript code
Client calls eval() on it Powerful and a little scary Hard to test
AJAX response contains JavaScript code
Client calls eval() on it Powerful and a little scary Hard to test
RJSRJS
Part of Ruby on Rails Generates JS via Ruby methods Not as scary as raw JS since it’s
coming from a tested library
Part of Ruby on Rails Generates JS via Ruby methods Not as scary as raw JS since it’s
coming from a tested library
Latency: The AJAX killerLatency: The AJAX killer
This AJAX stuff really is client-server communication
If the server is slow, your app crawls to a halt -- and inside a page, not just between pages
Less user feedback for a hung AJAX call Can lead to multiple clicks, reloads,
confusion, etc.
This AJAX stuff really is client-server communication
If the server is slow, your app crawls to a halt -- and inside a page, not just between pages
Less user feedback for a hung AJAX call Can lead to multiple clicks, reloads,
confusion, etc.
Solving LatencySolving Latency
The Easy Way: Use a spinny icon Still requires UI/UE design
The Easy Way: Use a spinny icon Still requires UI/UE design
Solving LatencySolving Latency
The Hard Way: To solve, you must write a lot of code on the
client to react immediately to user actions, then queue up requests and deal gracefully once the server reponds
Error handling Command queue Undo Dynamic/incremental data updates This leads naturally to true MVC in JS
The Hard Way: To solve, you must write a lot of code on the
client to react immediately to user actions, then queue up requests and deal gracefully once the server reponds
Error handling Command queue Undo Dynamic/incremental data updates This leads naturally to true MVC in JS
JS MVC ArchitectureJS MVC ArchitectureJSON
ModelObjects
Notify
ViewComponents
Render
DOMUser Events
Enqueue
CommandObjectsExecute / undo
AJAX (CGI)
Integration Testing with Selenium
Integration Testing with Selenium
Selenium… Runs in the browser Executes your app in a frame Simulates user actions via JavaScript Goes all the way to the server and back
Complementary to JSUnit JSUnit tests JS pages and libraries only, not interaction
with server Selenium is fun to watch Integrated into Continuous Integration like JSUnit Catch our talks on Wednesday (Selenium) and
Thursday (CI)
Selenium… Runs in the browser Executes your app in a frame Simulates user actions via JavaScript Goes all the way to the server and back
Complementary to JSUnit JSUnit tests JS pages and libraries only, not interaction
with server Selenium is fun to watch Integrated into Continuous Integration like JSUnit Catch our talks on Wednesday (Selenium) and
Thursday (CI)
Q&AQ&A
More ExamplesMore Examples
ClockClock
We wrote our JS “Mock Clock” test-first Actually, it’s a stub, not a mock :-)
Illustrates a true (isolated) unit test of a utility class
We wrote our JS “Mock Clock” test-first Actually, it’s a stub, not a mock :-)
Illustrates a true (isolated) unit test of a utility class
WeaverWeaver
The Weaver takes two arrays and returns the set of changes required to transform one into the other
Used by Tracker’s list widgets Illustrates use of roll-your-own test
spies
The Weaver takes two arrays and returns the set of changes required to transform one into the other
Used by Tracker’s list widgets Illustrates use of roll-your-own test
spies
DateWidgetDateWidget
We wrapped Yahoo’s YUI Calendar widget to add a text field with validation and pop-up-on-activate
Illustrates Testing with UI events Using a “demo.html” page Wrapping a third-party widget with
tests
We wrapped Yahoo’s YUI Calendar widget to add a text field with validation and pop-up-on-activate
Illustrates Testing with UI events Using a “demo.html” page Wrapping a third-party widget with
tests
Server ProxyServer Proxy
Illustrates AjaxUnit (ajax.js) mocking out the networking layer
Illustrates AjaxUnit (ajax.js) mocking out the networking layer
Add Note CommandAdd Note Command
Tests the command which adds a note (comment) to a story Does not test the networking layer,
just the command object Demonstrates use of stubs and
object mother (JsonFactory)
Tests the command which adds a note (comment) to a story Does not test the networking layer,
just the command object Demonstrates use of stubs and
object mother (JsonFactory)
Login WidgetLogin Widget
PeerToPatent uses page caching, so we have to render the “you’re logged in / please log in” area of the screen in JavaScript
PeerToPatent uses page caching, so we have to render the “you’re logged in / please log in” area of the screen in JavaScript
Cacheable FlashCacheable Flash
Not that kind of Flash… “Flash” is Rails for “Message Box”
In order for dynamic information to be rendered inside of a statically cached page, we put it into a cookie, and render the cookie’s contents with JavaScript
Not that kind of Flash… “Flash” is Rails for “Message Box”
In order for dynamic information to be rendered inside of a statically cached page, we put it into a cookie, and render the cookie’s contents with JavaScript
Cookie LibraryCookie Library
Illustrates testing a third-party library
We found bugs in it, esp. IE 7 We had to patch it
Illustrates testing a third-party library
We found bugs in it, esp. IE 7 We had to patch it
JS PaginatorJS Paginator
Illustrates receiving JSON with data Download (actually render) entire
data set on page load, then render some at a time Doesn’t scale to large datasets, like
Rico LiveGrid does
Illustrates receiving JSON with data Download (actually render) entire
data set on page load, then render some at a time Doesn’t scale to large datasets, like
Rico LiveGrid does
ExperimentationExperimentation
Since this is an agile conference, if there's time and interest, we can get volunteers from the audience to come up and pair program with the presenters on stage. solving problems posed by the audience.
Since this is an agile conference, if there's time and interest, we can get volunteers from the audience to come up and pair program with the presenters on stage. solving problems posed by the audience.
Where to find more info?Where to find more info?
http://jsunit.net http://pivotallabs.com
http://jsunit.net http://pivotallabs.com