KEMBAR78
Test-Driven JavaScript Development (JavaZone 2010) | PPTX
Test-Driven JavaScriptEliminating fear and chance from front-end web development
Christian Johansenhttp://cjohansen.no/http://github.com/cjohansenhttp://gitorious.org/~cjohansenhttp://twitter.com/cjno
My bookhttp://tddjs.com/
What we're doing todayHow to unit test JavaScript?
JavaScript testing challenges
Tool chain integrationHow to unit test JavaScript?
In-browser test frameworks
YUI TestPart of the YUI framework
Can test any code, regardless of framework
In-browser runner
Built-in mocks
Can ship results over the internet
Supports many output formats (JUnit XML, TAP, JSON ++)http://developer.yahoo.com/yui/3/test/
YUI Test case anatomy
YUI Test scaffolding
YUI Test run
YUI Test: The GoodEasy to get started
Run in any browser
Built-in mocks
Drop into app for integration testingYUI Test: The badBoilerplate HTML fixture
Manually test all browsersProblem: Impractical workflow
Headless runners
JSpecBDD framework
Runs in browser, Rhino and Node.js
Emulate DOM with env.js
Browser-based: Similar to YUI Test
JSpec Rhino scaffolding
JSpec Rhino run
JSpec + Rhino: The goodNo browsers
FastProblem: It's all fake
RhinoJust another runtimeNot like any browsers actually in use
env.jsJust another DOM implementationNot like any DOM implementation in actual use
I hear these are popular
...and these
Manual testing is time consuming
The best from both worlds
JsTestDriver
JsTestDriver.conf
Start JsTestDriver Serverjava -jar JsTestDriver-1.2.2.jar --port 4224
Capture Target Browsers
JsTestDriver Run
Bonus featuresAlternative assertion frameworks
Supports QUnit, YUI, Jasmine
JUnit XML Output
Coverage pluginCLI Helper$ gem install jstdutil$ export JSTESTDRIVER_HOME=~/bin/jstestdriver
Pretty colors
With errors
Also...$ jsautotestRuns affected tests on each save
Eclipse
Eclipse
Eclipse run
IntelliJ IDEA plugin also available
Just released
JavaScript testing challenges
XMLHttpRequestNeeds a server responding
Makes tests run slow(er)
Unsuitable for unit testsSolution: EncapsulateSimple and elegant
Looser coupling
Easy to testExample: Chat client
Anatomy
onSubmitmessageFormControllerthis.view (form)this.model (cometClient)messageListControllerthis.view (dl)this.model (cometClient)cometClient
What's the trick?All network access goes through cometClient
observable supports same API as cometClient
Use observable in testsWhat about cometClient?We'll get there
Event HandlersTouch, keyboard, mouse events
Cross-browser issues
Cumbersome to manually fireonSubmitmessageFormControllerthis.view (form)this.model (cometClient)messageListControllerthis.view (dl)this.model (cometClient)cometClient
Submitting message
Solution: Decouple codeSimple
Testable
Often makes sense API-wise
Testing event handlersVerify that setView adds event handler to form element for submit event
Verify that the handler is postMessage, bound to the controller
Test postMessage separatelyStubs and Mocks
Disclaimer: I wrote thathttp://cjohansen.no/sinon/
Sinon.JS SpiesWraps functions
Does not interrupt normal execution
Logs all calls and related dataUsing Sinon.JS spies
sinon.testCase()Automatically verifies mocks
Automatically restores all fakes
Provides useful utilities (more later)Verify that an event handler was added
Testing the handler
Testing event handlersVerify that setView adds event handler to form element for submit event
Verify that the handler is postMessage, bound to the controller
Test postMessage separately
Use an ad hoc stub
Integration: Simulate
Testing actual network accessUsing Sinon.JS
Configure a fake server
Fake JSON response{  "message": [{    "id": 1,    "user": "Johansen",    "message": "oh hai"  }],  "token": "1"}The cometClient format, an array of one new message
Force fake server to respond
What happened?GET /chat?1283370174112
Fake server recognizes /\/chat\?\d+/
this.server.respond(); fakes a response

Test-Driven JavaScript Development (JavaZone 2010)