Beverage Driven Development

Testing your Rails Javascript

with Mocha, Chai, and Konacha

 

by Brian Auton

Brian Auton

 

 

Developer and consultant at

Neomind Labs

Why JavaScript tests?

  • Clean code
  • Find regressions
  • Executable specification

JS test workflow

  • Real browser
  • Headless
  • Automated

Mocha

  • Configurable: dialects, assertions, output
  • Good asynchronous support
  • Works in browser or server-side

Mocha: Write a test

function closePopup() {
  $("#popup").remove();
}
suite("closePopup", function() {
  setup(function() {
    $("body").append("<div id='popup'></div>");
  });

  test("should close the popup", function() {
    closePopup();
    if($("#popup").is(":visible")) throw "failure";
  });
});

Mocha: Better with CoffeeScript

function closePopup() {
  $("#popup").remove();
}
suite "closePopup", ->
  setup ->
    $("body").append("<div id='popup'></div>");

  test "should close the popup", ->
    closePopup()
    throw "failure" if $("#popup").is ":visible"

Chai: Adding assertions

assert = chai.assert

suite "closePopup", ->
  setup ->
    $("body").append "<div id='popup'></div>"

  test "should close the popup", ->
    closePopup()
    assert.equal false, $("#popup").is(":visible")

Chai: should-style

should = chai.should()

suite "closePopup", ->
  setup ->
    $("body").append "<div id='popup'></div>"

  test "should close the popup", ->
    closePopup()
    $("#popup").is(":visible").should.equal false

Chai: expect-style

expect = chai.expect

suite "closePopup", ->
  setup ->
    $("body").append "<div id='popup'></div>"

  test "should close the popup", ->
    closePopup()
    expect($("#popup").is(":visible")).to.equal false

Mocha: Bdd-style

mocha.ui "bdd"
assert = chai.assert

describe "closePopup", ->
  beforeEach ->
    $("body").append "<div id='popup'></div>"

  it "should close the popup", ->
    closePopup()
    assert.equal false, $("#popup").is(":visible")

Mocha: Asynchronous

function closePopup(callback) {
  $("#popup").fadeOut(400);
}
assert = chai.assert

suite "closePopup", ->
  setup ->
    $("body").append "<div id='popup'></div>"

  test "should close the popup", (done)->
    closePopup()
    assert.equal true, $("#popup").is(":visible")
    setTimeout ->
      assert.equal false, $("#popup").is(":visible")
      done()
    , 500

Konacha

  • Handles HTML, routing, and server
  • Asset pipeline support
  • Clean environment for each test

Installing Konacha

group :test, :development do
  gem 'konacha'
end
Konacha.configure do |config|
  config.spec_dir = "test/javascripts"
end

With Poltergeist

 

http://phantomjs.org/download.html

group :test, :development do
  gem 'konacha'
  gem 'poltergeist', :require => "capybara/poltergeist"
end
Konacha.configure do |config|
  config.spec_dir = "test/javascripts"
  config.driver = :poltergeist
end

Konacha: our test

mocha.ui "tdd"
mocha.globals ["$"]
#= require test_helper
#= require application

suite "closePopup", ->
  setup ->
    $("body").append "<div id='popup'></div>"

  test "should close the popup", (done)->
    closePopup()
    assert.equal true, $("#popup").is(":visible")
    setTimeout ->
      assert.equal false, $("#popup").is(":visible")
      done()
    , 500

Run it

$ rake konacha:run
.

Finished in 0.41 seconds
1 examples, 0 failed, 0 pending

Better Rake Syntax

task "test:javascripts" => "konacha:run"
task "test" => [
  "test:units",
  "test:functionals",
  "test:integration",
  "test:javascripts"
]
$ rake test:javascripts
$ rake

Issues

  • Vendored JS libraries
  • Stack trace handling

Testable JS code

For another talk...