How to Remove Document Management Hurdles with X-Docs?
SF jQuery Conf: Packaging Code With Testable J Query Plugins
1. Packaging Your Code with
Testable jQuery Plugins
Andy Peterson
Testing, Plugins, Integrating, etc
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
2. You
jQuery Plugins?
Tested Javascript?
Selenium or other UI tests?
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
3. Andy Peterson
started with commercial & enterprise
desktop software (Mac), then consulting
agile practices and disciplines
Javascript-- dabbled for the last few
years, but over the last 9 months, feel like
it’s become a part of my professional
practice
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
4. Software services company with offices in San
Francisco and Los Angeles. 20 people
We're an agile shop, focus on RoR & Java webapp
dev along with more mobile
1/2 our work is bootstrapping startups
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
5. Why am I here?
We didn't have a good story about Javascript
testing
Which led us to develop practices
Which fed back into how we approach
Javascript coding in general
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
6. Agenda
Javascript testing is hard. Why?
demo
The Trick: UNIT test + Plugins
problems & solutions
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
7. We Don’t Try
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
8. Why We Don’t Try
Never did it before
Had a bad experience
Patterns unclear
No infrastructure
App’s not complicated enough
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
9. Too May Tools
http://www.slideshare.net/jeresig/understanding-javascript-testing
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
10. Nobody Else Does
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
11. And when we tried...
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
12. page.[dynamic]
<html>
<head>
<link href=”site.css” type=”text/stylesheet” />
<script src=‘page.js’ type=”text/javascript”>
</script>
<body>
<h1>Joe’s Page</h1>
...
</body>
</html>
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
13. page.js
$(function() {
$(‘h1’).css(‘color’,‘red’);
});
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
14. Test on the Page?
Screw.Unit(function() {
it(‘should change to red’, function() {
expect($(‘h1’).css(‘color’)).to(equal,‘red’);
});
});
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
15. Brings Up Lots of Qs
How do I get the markup (if dynamic)?
“on ready” timing
What happens when other things happen on
this page? As it evolves?
How do I know it really did something?
(Pre-conditions)
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
16. Whew!
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
17. Why is it so
complicated?
We’re trying to build integration tests--
page markup + lifecycle
multiple javascript sources
Lots of pieces to integrate
browsers, test engines, integration servers,
developer machines
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
18. Simplify???
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
19. Plugins + Unit Test
it’s a good solution to lots of problems
20. Plugins Solve
Separate from “on doc ready” timing
clear testing pattern
provide good modularity pattern
decoupled, abstract, encapulation
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
21. Plugins are Easy to
Write
(function($) {
$.fn.myPlugin = function(req, opts) {
return this.each(function() {
$(this).myStuff...
});
}
})(jQuery);
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
22. jQuery Plugin Writing
http://docs.jquery.com/Plugins/
Authoring
http://www.learningjquery.com/
2007/10/a-plugin-development-
pattern
look at event patterns
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
23. Unit Testing Solves
“test one piece at a time”
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
24. Requirements for a Tool...
Easy to Install
instant feedback & fast for TDD or BDD
reliable & automate(able)
mocking & stubbing
minimal paradigm or language shift
any browser
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
25. Which Javascript Testing
Library?
Lots to choose from
QUnit
xUnit Style: JSUnit, YUITest, etc.
BDD Style: JSSpec, JSpec,
Jasmine, ScrewUnit
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
26. We Picked Blue Ridge
(not by me)
Conventions (+ Rails Tools)
ScrewUnit (BDD)
in & out of browser with Rhino + env.js
Provides: TDD, Reliable, headless, little
paradigm shift, now multi-browser
NOTE: maven plugin too!
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
27. Install
./script/plugin install git://github.com/relevance/blue-
ridge.git
./script/generate blue_ridge
rake test:javascripts
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
28. How do we test
makeRed?
Javascript UNIT tests
source markup (fixture) +
code under test
= reliable, easy entry
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
29. Rewrite as Plugin
jquery.make_red.js
jQuery.fn.makeRed = function() {
this.css(‘color’,‘red’);
});
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
30. HTML “Fixture” for Test
fixtures/make_red.html
<head>
<title>Make Red Results</title>
<link rel="stylesheet" href="screw.css" type="text/css"
charset="utf-8"/>
<script type="text/javascript" src="../../../vendor/plugins/
blue-ridge/lib/blue-ridge.js"></script>
</head>
<body>
<h1 style=‘color: pink’ >gotta test...</h1>
</body>
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
31. Test with “Fixture”
/ make_red_spec.js
/
require("spec_helper.js");
require(".../make_red.js"); / code we are testing
/
Screw.Unit(function() {
before(function() {
expect($(‘h1’).css(‘color’)).to_not(equal,‘red’);
" $('h1').makeRed();
});
it(‘should change to red’, function() {
expect($(‘h1’).css(‘color’)).to(equal,‘red’);
});
});
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
32. Test with “Fixture”
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
33. Test with “Fixture”
$ rake spec:javascript
(in /Users/ndp/demo)
Running application_spec.js with fixture 'fixtures/
application.html'...
.
1 test(s), 0 failure(s)
0.076 seconds elapsed
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
34. Link to on Page
<html>
<head>
<script src=”.../jquery.make_red.js” />
<script type=”text/javascript”>
$(function() {
$(‘h1’).colorRed();
});
</script>
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
35.
36. Implications of Unit
Testing
(over Integration Testing)
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
37. Must modularize code
(using events is a good approach)
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
38. Build “Fixture” HTML
seems a little counter-intuitive
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
39. Alternatives to Fixtures
generate dynamically w/ Javascript
generate -- figure out how to render your
views
“bake out” -- eg.
http://pivotallabs.com/users/jb/blog/
articles/1152-javascripttests-bind-reality-
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
40. May need simple
integration tests anyhow
(but much less)
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
42. Remember
Javascript PLUGIN
Fixture (Input) HTML markup
Test of Plugin
Individual Units
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
43. Unit Tests
Perfect for
DOM Manipulation
Business rules
(This gets you 80-90% there!)
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
44. Plugin Becomes Complex
Use Tests to aid in Refactoring
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
45. On Doc Ready Big
Can use “controller” pattern
Can use “composing” plugin
(not testing doc ready)
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
46. Ajax
EZ: Stub/Mock out $.ajax
$.ajax = function(url, fn) {
/ assert called correctly
/
}
test success: fn separately
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
47. Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
48. How to test?
$.fn.clockify = function() {
setInterval(function() {
this.html(Time().toString());
}, 1000);
}
...
/ test:
/
describe('clockify', function() {
before(function() {
$('p').clockify();
});
it('should update every second', function() {
/ ???
/
});
});
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
49. Separate setInterval()
$.fn.clockify = function() {
var $j = this;
setInterval(function() {
$j.updateClock();
}, 1000);
return this;
}
$.fn.updateClock = function() {
this.each(function(i, e) {
$(e).html((new Date()).toString());
});
}
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
50. Separate setInterval()
describe('clockify', function() {
before(function() {
setInterval = mock_function(setInterval);
});
it('should call setInterval with 1000', function() {
setInterval.should_be_invoked().
with_arguments(null, 1000).exactly(1);
$('p').clockify().html('running');
});
});
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
51. Or Even...
$.fn.interval = function(fn, millis) {
var $this = this;
setInterval(function() {
$this.each(function() {
fn.apply(this);
}, millis);
return this;
}
$.fn.updateClock = function() {
return this.html((new Date()).toString());
}
$.fn.clockify = function() {
return this.interval(function() { $(this).updateClock(); }, 1000);
}
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
52. Testing Animations Opts
skip
stub
use integration testing tools
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
53. Animations: Turning Off
jQuery.fx.off = true
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
54. Animations: Configure
$.fn.masterRadios = function(settings) {
settings = $.extend({},
$.fn.masterRadios.defaults,
settings);
...
$form.find('.spesh').each(function(index, elem) {
settings.showFn.call($(elem));
});
}
$.fn.masterRadios.defaults = {
hideFn: jQuery.fn.hide,
showFn: jQuery.fn.show
};
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
55. Input Markup is Complex
(therefore don’t want “fixture”)
Simplify
Semantic Markup?
Can Generate within Javascript (Safely)
Separate Concerns
Refactor?
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
56. Markup Evolves
Less of a problem than it seems
Sometimes you just need integration tests
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
57. Drag & Drop
Don’t try to script whole drag process
Unit test draggable, droppable
Events
Trigger & Bind
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
58. Plugins “Draw Inside the
Lines”
$(‘div.specific’).plugin()
shouldn’t affect other
nodes in the doc
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
59. Expect No Change
<html>
<body>
<div class=‘control’>
...
<div id=‘fixture’>
...
<div class=‘control’>
...
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
60. Expect No Change
var control; / remember!
/
before(function() {
control = $('.control').html();
});
after(function() {
expect($('.control').html()).to(equal,
control);
});
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
61. Unit Tests v. Small Files
Keep code modular
Put units of JS in their own files (LOTS)
Share small JS with
HTML pages
Tests
Lots of options to combine at deploy time!
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
62. CSS?
Include CSS in tests
Helps visualize
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
63. What about JS Classes?
Hmm...
Use
object literals
closures
DOM nodes
Is this enuf?
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
64. Limitations
Not testing doc ready
Nor Markup generation
Nor integration of the two
THUS: Selenium-- but simpler
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
65. Other Benefits
(besides testing)
Can move HTML generation where it is most
appropriate
Can move business logic to client
Less “chatty” apps
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
66. Our Experience
3 projects
definitely a net value add
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
67. Summary
prefer UNIT tests over
INTEGRATION tests for ease
of testability and
development
package as jQuery Plugins
work through tricky problems
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
68. Carbon Five
We get to work on diverse projects and learn lots
of new technologies.
We're always looking for both seasoned
developers and junior programmers to work,
please feel free to talk to me now or email me
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
69. Thanks!
andy@carbonfive.com
http://github.com/ndp
Andy Peterson • andy@carbonfive.com • SF jQuery Conf • April 2010 Copyright (c) 2010 Andrew J. Peterson
Editor's Notes
Boy meets girl joke
Did it really do something? Preconditions?
On Load timing
What if the markup is dynamic
As the page grows, how is this going to work?
Claim only a single name in the jQuery namespace
Accept an options argument to control plugin behavior
Provide public access to default plugin settings
Provide public access to secondary functions (as applicable)
Keep private functions private
Support the Metadata Plugin
There are others... this one is good
- PUT FILES HERE
_ WRITE TST HERE
OPEN IN BROWSER
RUN ON COMMAND LINE