Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
681 lines (527 sloc) 15.3 KB
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
<head>
<title>A Survey of Behavior Driven Development in JavaScript</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="stylesheets/main.css" type="text/css" media="screen" charset="utf-8" />
<script type="text/javascript" src="javascripts/jquery.slideshow/vendor/jquery.js"></script>
<script type="text/javascript" src="javascripts/jquery.slideshow/lib/jquery.slideshow.js"></script>
<script type="text/javascript">
$(function() {
$('.slideshow').slideshow({format: 'presentation'});
});
</script>
</head>
<body>
<div class="slideshow">
<div class="first slide">
<h1>A Survey of Behavior-Driven Development in JavaScript</h1>
<br/><br/>
<br/><br/>
<br/><br/>
<h2>T.J. VanSlyke</h2>
<div class="center">
<img src="images/elc.jpg"/>
</div>
<h2>September 4, 2008</h2>
</div>
<div class="slide">
<h1>describe "Presentation" do<br/>
it "has an overview"
</h1>
<ul>
<li>Motivation</li>
<li>Background</li>
<li>Technical hurdles to a friendly solution.</li>
<li>Existing solutions</li>
<li>The future</li>
<li>Community forum</li>
</ul>
</div>
<div class="slide">
<h1>Motivation</h1>
<ul>
<li>No integration of JavaScript testing into developer workflow</li>
<li>Selenium doesn't cut it for day-to-day unit testing</li>
<li>Client-side code is becoming more complex</li>
</ul>
</div>
<div class="slide">
<h1>Motivation</h1>
<ul>
<li>Yet another reason to wake up with keyboard imprints on your face.</li>
</ul>
<div class="center">
<img src="images/passed_out_on_keyboard.gif"/>
</div>
</div>
<div class="slide">
<h1>jSpec</h1>
<div class="center">
<img src="images/jspec.png"/>
</div>
<h2>Features!</h2>
<ul>
<li>Message expectations with quantifiers like RSpec</li>
<li>RSpec-like 'should(Not)' predicate syntax</li>
<li>Nested describe blocks (a la RSpec)</li>
<li>No originality whatsoever</li>
</ul>
</div>
<div class="slide">
<h1>jSpec: Technical hurdles</h1>
<p>JavaScript can perform similarly to Ruby when it comes to syntax-sugar metaprogramming.</p>
<pre>
# Ruby
class Object
def should(matcher, &amp;block)
do_stuff_with(matcher, block)
end
end
</pre>
<pre>
// JavaScript
Object.prototype.should = function(matcher, fn) {
doStuffWith(matcher, fn);
}
</pre>
</div>
<div class="slide">
<h1>jSpec: Technical hurdles</h1>
<p>But unfortunately, the grass isn't greener on the client-side.</p>
<pre class="left">
# Ruby
class Object
def say_hello
puts "Hello!"
end
end
&gt;&gt; nil.a_method
Hello!
</pre>
<pre class="right">
// JavaScript
Object.prototype.aMethod = function() {
// do stuff
}
&gt;&gt; null.aMethod();
TypeError: null has no properties.
</pre>
</div>
<div class="slide">
<h1>jSpec: Technical hurdles</h1>
<p>&quot;Solution&quot;: Use a custom selector for all expectations.</p>
<pre>
jSpec.$ = function(expr)
{
if (expr == null)
{
return new jSpec.NullObject();
}
return expr;
}
</pre>
</div>
<div class="slide">
<h1>jSpec</h1>
<pre>
jSpec.describe('this presentation', function($)
{
var presentation;
$.beforeEach(function() {
presentation = { audience: null, boring: false };
});
$.it("should have an audience", function() {
$.$(presentation.audience).shouldNot().beNull();
});
$.it("should not be boring", function() {
$.$(presentation).shouldNot().be('boring');
});
});
</pre>
</div>
<div class="slide">
<h1>jSpec: Message Expectations</h1>
<h2>Example</h2>
<pre>
jSpec.describe('this presentation', function($)
{
var presentation;
$.beforeEach(function() {
presentation = {
begin : function() { alert(this.applause); },
applause: 'woooo!'
};
});
$.it("should receive applause", function() {
$.$(presentation).shouldReceive('applause');
presentation.begin();
});
});
</pre>
</div>
<div class="slide">
<h1>RTFG!</h1>
<div class="center">
<img src="images/ninja-kitten-defeats-dog-with-google-search-skills-always.jpg"/>
</div>
</div>
<div class="slide">
<h1>Other packages</h1>
<ul>
<li>Firebug (JavaScript assertions, http://getfirebug.com)</li>
<li>JsUnit (http://www.jsunit.net/)</li>
<li>QUnit (http://docs.jquery.com/QUnit)</li>
<li>JSSpec (http://code.google.com/p/jsspec/)</li>
<li>Screw.Unit (http://github.com/nkallen/screw-unit/tree/master)</li>
</ul>
</div>
<div class="slide">
<h1>Firebug</h1>
<ul>
<li>Provides a console API for simple assertions</li>
<li>Does <em>not</em> provide a test harness for automation</li>
<li>One platform only (Firefox)</li>
</ul>
</div>
<div class="slide">
<h1>JsUnit</h1>
<h2>From the JsUnit documentation:</h2>
<pre>
function testWithValidArgs() {
assertEquals("2 times 3 plus 5 is 11", 11,
multiplyAndAddFive(2, 3));
assertEquals("Should work with negative numbers", -15,
multiplyAndAddFive(-4, 5));
}
function testWithInvalidArgs() {
assertNull("A null argument should result in null",
multiplyAndAddFive(2, null));
assertNull("A string argument should result in null",
multiplyAndAddFive(2, "a string"));
}
</pre>
</div>
<div class="slide">
<h1>QUnit</h1>
<h2>From the QUnit documentation:</h2>
<pre>
module("Module A");
test("first test within module", function() {
ok( true, "all pass" );
});
test("second test within module", function() {
ok( true, "all pass" );
});
</pre>
</div>
<div class="slide">
<h1>JSSpec</h1>
<h2>From the JSSpec documentation:</h2>
<pre>
describe('Plus operator', {
'should concatenate two strings': function() {
value_of("Hello " + "World").should_be("Hello World");
},
'should add two numbers': function() {
value_of(1 + 2).should_be(3);
}
})
</pre>
</div>
<div class="slide">
<h1>Screw.Unit</h1>
<h2>From the Screw.Unit README:</h2>
<p>Screw.Unit is a Behavior-Driven Testing Framework for Javascript. It features nested describes. Its goals are to provide:</p>
<ul>
<li>a DSL for elegant, readable, organized specs;</li>
<li>an interactive runner that can execute focused specs and describes;</li>
<li>and brief, extensible source-code.</li>
</ul>
</div>
<div class="slide">
<h1>Screw.Unit</h1>
<div class="center">
<img src="images/turtle.jpg"/>
<h2>RADICAL!</h2>
</div>
</div>
<div class="slide">
<h1>Screw.Unit</h1>
<div align="center">
<img src="images/screw-unit-runner.png"/>
</div>
</div>
<div class="slide">
<h1>Screw.Unit</h1>
<pre>
describe('this presentation', function() {
var presentation;
before(function() {
presentation = { audience: null, boring: false };
});
it("should have an audience", function() {
expect(presentation.audience).to_not(be_null);
});
it("should not be boring", function() {
expect(presentation.boring).to_not(be_true);
});
});
</pre>
</div>
<div class="slide">
<h1>Screw.Unit: Time to get meta up in here.</h1>
<pre>
describe("jquery.slideshow", function() {
describe('nextSlide()', function() {
});
describe('previousSlide()', function() {
});
});
</pre>
</div>
<div class="slide">
<h1>Screw.Unit: Time to get meta up in here.</h1>
<pre>
describe('nextSlide()', function() {
it('increments the slide index', function() {});
it('hides the previous slide', function() {});
it('shows the next slide', function() {});
});
</pre>
</div>
<div class="slide">
<h1>Screw.Unit: Time to get meta up in here.</h1>
<pre>
...
it('increments the slide index', function() {
expect(slideshow.slideIndex()).to(equal, 0);
slideshow.nextSlide();
expect(slideshow.slideIndex()).to(equal, 1);
});
...
</pre>
</div>
<div class="slide">
<h1>Screw.Unit: Time to get meta up in here.</h1>
<pre>
it('hides the current slide', function() {
});
</pre>
</div>
<div class="slide">
<h1>Screw.Unit.shouldReceive('messageExpectations')</h1>
<h2>In an ideal world:</h2>
<pre>
it('hides the current slide', function() {
expect(slides[0]).to(receive, 'hide'); // ideal...
slideshow.nextSlide();
});
</pre>
</div>
<div class="slide">
<h1>Screw.Unit.shouldReceive('messageExpectations')</h1>
<h2>In the real world:</h2>
<pre>
...
it('increments the slide index', function() {
expect(slideshow.slideIndex()).to(equal, 0);
slideshow.nextSlide();
expect(slideshow.slideIndex()).to(equal, 1);
});
...
</pre>
</div>
<div class="slide">
<h1>Let's look under the hood...</h1>
<pre>
be_true: {
match: function(expected, actual) {
return actual;
},
failure_message: function(expected, actual, not) {
return 'expected ' + $.print(actual) + (not ? ' to not be true' : ' to be true');
}
},
...
</pre>
<div class="notes">
In order to create a 'receive' matcher, the architecture would need to be changed
to accommodate for post-test callbacks such that we could reconcile the expectations
at the end of the test execution.
</div>
</div>
<div class="slide">
<h1>Stubs and Mocks?</h1>
<ul>
<li>JSMock</li>
<li>Roll your own</li>
</ul>
</div>
<div class="slide">
<h1>JSMock</h1>
<pre>
function Presentation() {
this.boring = function() { /* ... */ };
this.audience = function() { /* ... */ };
}
</pre>
</div>
<div class="slide">
<h1>JSMock</h1>
<pre>
function test_PresentationIsTotallyStellar() {
var mockControl = new MockControl();
presentationMock = mockControl.createMock(Presentation);
var soManyPeople = [ 'all', 'of', 'you', 'beautiful', 'rails', 'enthusiasts' ];
presentationMock.expects().boring().andReturn(false);
presentationMock.expects().audience().andReturn(soManyPeople);
}
</pre>
</div>
<div class="slide">
<h1>JSMock: Yay/Nay</h1>
<h2>Yay!</h2>
<ul>
<li>Expectation recording/matching!</li>
</ul>
<h2>Nay!</h2>
<ul>
<li>Mildly cumbersome syntax</li>
</ul>
</div>
<div class="slide">
<h1>Mock schnell!</h1>
<h2>Or: One Reason To Love Prototype-Based Languages</h2>
<pre>
// A la RSpec mock()
var mock_schnell = {
field: 'value',
another: 123,
method: function() { return 'some mocked value'; }
};
</pre>
</div>
<div class="slide">
<h1>Roll your own: Yay/Nay</h1>
<h2>Yay!</h2>
<ul>
<li>No external libraries and no setup beyond a plain ol' JavaScript object.</li>
</ul>
<h2>Nay!</h2>
<ul>
<li>No expectation recording/matching</li>
</ul>
</div>
<div class="slide">
<h1>Technical hurdles to be solved</h1>
<ul>
<li>No automated cross-browser support</li>
<li>No integration into Rails development workflow (i.e., rake spec)</li>
<li>No mature message expectation engine</li>
<li>No mature mocking/stubbing library</li>
</ul>
</div>
<div class="slide">
<h1>THE FUTURE</h1>
<div class="center">
<img src="images/flyingcar.jpg"/>
</div>
</div>
<div class="slide">
<h1>THE FUTURE</h1>
<h2>HotRuby</h2>
<h3>(a.k.a. "We don't need no stinkin' JavaScript!")</h3>
<ul>
<li>JavaScript virtual machine for Ruby</li>
<li>Runs opcode compiled by YARV (Yet Another Ruby VM)</li>
<li>Developed by Yu Kobayashi</li>
<li>Most grammars are implemented, but it currently lacks exception handling and the majority of Ruby's built-in functions and classes</li>
</ul>
<div class="notes">
</div>
</div>
<div class="slide">
<h1>HotRuby</h1>
<h2>Maybe someday instead of this:</h2>
<pre>
(function($) {
$.fn.slidesnow = function() {
// ...
};
)(jQuery);
</pre>
<h2>We'll do this:</h2>
<pre>
dom_function :slideshow do |element|
# ...
end
</pre>
</div>
<div class="slide">
<h1>HotRuby</h1>
<pre class="left">
describe "my sweet client-side
Ruby slideshow app" do
before(:each) do
@slideshow = tag(:div).slideshow!
@slides =
[
tag(:div, class => 'slide'),
tag(:div, class => 'slide'),
tag(:div, class => 'slide')
]
@slideshow.stub!(:slides).
and_return(@slides)
end
</pre>
<pre class="right">
describe "next_slide" do
it "hides the current slide" do
@slides[0].should_receive(:hide)
@slideshow.next_slide
end
it "increments the slide index" do
@slideshow.slide_index.should == 0
@slideshow.next_slide
@slideshow.slide_index.should == 1
end
it "shows the current slide" do
@slides[1].should_receive(:show)
@slideshow.next_slide
end
end
end
</pre>
<div class="notes">
Here is what client-side RSpec <em>could</em> look like...
Note that there isn't much here you couldn't do with RJS; however, also note that
this gives the flexibility to fully-spec running client-side code.
</div>
</div>
<div class="slide">
<h1>HotRuby: Problems to solve</h1>
<ul>
<li>We need a full Ruby implementation.</li>
<li>How do we access the DOM? DOM Events?</li>
<li>How can we integrate JavaScript tests into our daily rake tasks?</li>
<li>How can we accommodate for multiple JavaScript implementations?</li>
</ul>
</div>
<div class="slide">
<h1>Your turn!</h1>
<div class="center">
<img src="images/elmo.jpg"/>
</div>
<h2>What are <em>you</em> using to test your JavaScript?</h2>
</div>
<div class="slide">
<h1>FIN.</h1>
<div class="center">
<img src="images/mrrogers.jpg"/>
</div>
<h2>github.com/teejayvanslyke/railsconfeurope2008</h2>
</div>
</div>
</body>
</html>