48
Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit Authors: Alex Chaffee, Pivotal Labs Edward Hieatt, Pivotal Labs

Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Embed Size (px)

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.

Citation preview

Page 1: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 2: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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.

Page 3: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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)

Page 4: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 5: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 6: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 7: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 8: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Testing StrategiesTesting Strategies

Unit Testing: JSUnit

Integration Testing: Selenium WATIR

Unit Testing: JSUnit

Integration Testing: Selenium WATIR

Page 9: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 10: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 11: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

JavaScript Tools and Libraries

JavaScript Tools and Libraries

Page 12: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Firefox ExtensionsFirefox Extensions

Web Developer Firebug ROCKS

Web Developer Firebug ROCKS

Page 13: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Development EnvironmentsDevelopment Environments

IDEA Eclipse TextMate ?

IDEA Eclipse TextMate ?

Page 14: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 15: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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.

Page 16: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 17: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 18: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Pivotal AssertionsPivotal Assertions

Page 19: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 20: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 21: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 22: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Client-Server Communication

Client-Server Communication

Page 23: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 24: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 25: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 26: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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] }}

Page 27: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 28: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 29: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 30: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 31: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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.

Page 32: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 33: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 34: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

JS MVC ArchitectureJS MVC ArchitectureJSON

ModelObjects

Notify

ViewComponents

Render

DOMUser Events

Enqueue

CommandObjectsExecute / undo

AJAX (CGI)

Page 35: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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)

Page 36: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Q&AQ&A

Page 37: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

More ExamplesMore Examples

Page 38: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 39: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 40: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 41: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Server ProxyServer Proxy

Illustrates AjaxUnit (ajax.js) mocking out the networking layer

Illustrates AjaxUnit (ajax.js) mocking out the networking layer

Page 42: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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)

Page 43: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 44: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 45: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 46: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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

Page 47: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

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.

Page 48: Writing and Testing JavaScript-heavy Web 2.0 apps with JSUnit

Where to find more info?Where to find more info?

http://jsunit.net http://pivotallabs.com

http://jsunit.net http://pivotallabs.com