Skip to main content
Test-Driven JavaScript
with ScrewUnit &
BlueRidge	
Andy Peterson
Jonah Williams
Carbon Five
Introductions
Carbon Five

Boutique software consulting
  20 people, all developers
  Agile development, collaboration, transparency
  Mix of projects: start-ups / non-profits / enterprise
  History of Java, but now RoR + misc.
Agenda

 JavaScript unit testing: how & why
  BDD, jQuery, plugins
Demo TDD with a simple test
Problems & lessons learned
Q&A
Why NOT JS Unit Testing?
Can’t get started
We don’t; nobody else does
Patterns unclear-- what does it look like
Too many tools (and arch.) to choose from
  ... lack of tool & infrastructure support
Flakey/bad experiences
Why JS Unit Testing?

Tests => Confidence => Ambition
Take on more challenging requirements
More homogenous architecture (w/o Flash)
Push logic into browser... scalability
Refactor reliably
JS Testing Universe
 Styles of Tests:
   xUnit: JSUnit, QUnit, YUITest, etc.
   BDD: JSSpec, ScrewUnit
 Automation:
   In-browser w/ Selenium, WebDriver, JSTestDriver
   Out of browser: Env.js + Rhino
Blue Ridge
Ruby on Rails plugin (therefore conventions!)
Integrates
  ScrewUnit (BDD) and Smoke (mocking)
  Glue code and scripts
  Rhino & Env.js to run out of browser
from Larry Karnowski, Runcoderun
BDD
Language closer to user stories
Popularized by RSpec in Ruby
describe -- organize
before / after blocks
Nesting describes -- to DRY
One assert should per test example
describe Stack do
  before(:each) do
    @stack = Stack.new
    @stack.push :item
  end
  describe "#peek" do
    it "should return the top element" do
      @stack.peek.should == :item
    end
    it "should not remove the top element" do
      @stack.peek
      @stack.size.should == 1
    end
  end
  describe "#pop" do                        Stack (generated docs)
    it "should return the top element" do    #peek
      @stack.pop.should == :item             - should return the top element
    end
    it "should remove the top element" do
                                             - should not remove the top
      @stack.pop                            element
      @stack.size.should == 0                #pop
    end                                      - should return the top element
  end
end                                          - should remove the top element
BDD	 in RSpec
describe 'a man' {
  before {
    @man = Man.new :luck=>5
  }

  describe '#decrement_luck' {
    it "decrements the luck field by the given amount" {
      @man.decrement_luck(3)
      @man.luck.should == 2
    }
  }
  ...

});
BDD	 in ScrewUnit
describe('Man', function() {
  var man;
  before(function() {
    man = new Man({luck: 5});
  });

  describe('#decrement_luck', function() {
    it("decrements the luck field by the given amount", function() {
      man.decrement_luck(3);
      expect(man.luck()).to(equal, 2)
    });
  });
  ...

});
Let’s See It
Wizard-izer (our example)

 Given a form comprised of fieldsets
   Creates a “panel” for each fieldset
   Hides all but the first panel
   Adds buttons to navigate panels
   etc.
Blue Ridge Installation
http://github.com/relevance/blue-ridge/tree/master




   # Within a rails project

   > ./script/plugin install git://
   github.com/relevance/blueridge.git
   > ./script/generate blue_ridge
   > ./script/generate javascript_spec wiz
Maven Installation
http://code.google.com/p/javascript-test-maven-plugin/

   <plugin>
  <executions><execution>
  <goals>
    <goal>javascript-test</goal>
  </goals>
  </execution></executions>
  <groupId>com.carbonfive.javascript-test</groupId>
  <artifactId>javascript-test-maven-plugin</artifactId>
  <version>1.0-beta1</version>
  <configuration>
    <includes>
       <include>src/test/javascript/suite*.html</include>
    </includes>
  </configuration>
</plugin>
3 Components
Fixture - markup that JS requires
  test/javascript/widget.html
Spec - tests
  test/javascript/specs/widget_spec.js)
Code to test - independent JS
  <public>/javascript/widget.js
jQuery

Functional DOM manipulation
One function $: $(selector) eg. $(‘p.cls’)
Chainable -- $
(‘p.cls’).addClass(‘abc’).removeClass(‘cls’).etc(...)

Plugins
jQuery Plugin
Add function to         jQuery.fn.reverse() = function() {
jQuery object
                            // “this” is list of nodes
Receive list of nodes
                            $(this).each(function() {
                               var t = $(this).text();
Do your stuff                  $(this).text(t);
                            }
Return “this”
                            return this;
Good tutorials online   }
DOM cleanup	

Tools don’t reset your DOM automatically
They are writing status back to the page (yuck!)
Solution
  Wrap fixtures (in #fixture container)
  before function that restores DOM state
DOM cleanup fn
// before(function() { $('#fixture).fixture(); });
jQuery.fn.fixture = function() {
    this.each(function() {
      if (this.original) {
          $(this).html(this.original);
      } else {
          this.original = $(this).html();
      }
    });
}
Put CSS in Fixture


 Helps with debugging
 Use FireBug to inspect
Structure as a jQuery plugin

jQuery is not required-- but really helps you organize
code
Separate document.ready() from code--
dependency injection
Use as organizing principal, and dependency injection
Write modular code, instead page-specific code
Learnings
Not Hard to Make JS
Testing a Part of Your
Process
Markup Design


Generated in JS? On the server? Hybrid?
Unit testing encourages generating more markup in
JavaScript
Will affect SEO and accessibility (good or bad)
When to test

Some JS can be experimental
Test-drive when you have a good sense of what he
component is
Testing is a refactoring tool
  As code gets complicated, pull into testable units
Don’t Be Afraid of JS


 Reminder wizard
 Method chooser demo-- data driven JS, with ajax
 structure
What to Unit Test /
Limitations
                                     AJAX
     JavaScript Unit Testing
                                  setTimeout()
                                  setInterval()
   DOM                 Business
 Manipulation           Logic     Animations

                                  Computed sizes
It’s Not Browser Testing

 ...it’s JS unit testing
   Not cross-browser testing
   Not your production markup
   Functional test framework still have a place
Our Testing Stack

  Selenium Integration & Browser Testing 1/10th

 Controller/View Integration
          Tests 1/5th
                                  Javascript Unit
                                     Testing
Model Unit     Controller Unit          1/10th

  Tests            Tests
JavaScript Testing
Yes We Can!	
Andy Peterson        Jonah Williams
andy@carbonfive.com   jonah@carbonfive.com
skype: ndpsoft
Blue Ridge TODOs
Run in more browsers (not just Firefox)
Speed
Events
Driving in multiple browsers
Using code served from app
Shenandoah
Other Options


ScrewUnit Server
Selenium + JSUnit
testswarm (?)
References (Bibliography)
 http://github.com/relevance/blue-ridge
 http://pivotallabs.com/users/nick/blog/articles/455-
 better-javascript-testing-through-screwunit
 http://github.com/ndp/wizardize
 http://www.slideshare.net/jeresig/understanding-
 javascript-testing
 http://code.google.com/p/javascript-test-maven-
 plugin/