Javascript BDD with Cucumber and Harmony

Inspired by tooky‘s recent post Exploring Harmony for javascript BDD with RSpec i have become determined to become better at writing javascript in a BDD kind of way.

On Monday we were privileged to have Corey Haines visit Eden for the day. I paired with Corey for a couple of hours learning about BlueRidge for unit-testing javascript classes in a Rails application. I liked what i learned but i am disappointed by the need to maintain HTML fixtures. I would like to run the specs directly on my application.

I was pairing with Tris today and we really wanted to run javascript straight from Cucumber scenarios, so we had another look at Harmony. The first thing we noticed that it needed to load a file (rather like a fixture), which we didn’t like very much. We went to see how HolyGrail does it.

Maybe it’s because we’re using Capybara not Webrat, or maybe it’s because we did something wrong, but we couldn’t get HolyGrail to work right. However, a quick peek at the source code gave us a few clues. We pulled out and tweaked the following:

def js(code)
  @__page ||= Harmony::Page.new(page.body.to_s)
  @__page.execute_js(code)
end

This worked pretty nicely for a step involving simple javascript, like:

Then /^the page is titled "([^\"]*)"$/ do |title|
  js('document.title').should == title
end

It was actually pretty exciting when we first saw that working! On to something more meaningful …

  Scenario: Use javascript to hide a box
    Given I am on a page with a box that can be hidden
    When I use javascript to click "Hide"
    Then the box should be hidden

Clicking the link with javascript actually wasn’t hard:

When /^I use javascript to click "([^\"]*)"$/ do |text|
  js("$('a:contains(#{text})')").click
end

We used jQuery to find the right link and click it using Harmony. Harmony sends the ‘click’ method straight through to the javascript object, which is pretty cool.

Ensuring the box disappears was a little more tricky. For the moment we’re just checking that the contents becomes empty.

Then 'the box should be hidden' do
  js("$('.hide_box')").html.should be_nil
end

Sure enough, when we plug in the unobtrusive jQuery code to bind the click and remove the box, the feature passes! Alright, this is a very small step down a long road, but to me it is very exciting indeed!

Next steps: we have already begun writing a Capybara driver to run Harmony. So that we don’t have to write a separate step definition When I use javascript to click “Hide” but we simply use the standard When I follow “Hide” and we tag the scenario with @harmony to use the alternative driver. That will first attempt to click it with javascript and, if nothing happens, it will follow the link in the normal way. We will also use the driver to handle when the @__page cached copy gets refreshed.

An important next step is make it a proper integration test, interacting with the full stack, such as clicking something which triggers an AJAX request to the server which must run a bit of code and send the response back to the page. I can foresee this presenting some challenges, we’ll see!

Exciting things are happening, and this is just the beginning! Web development these days is all about standing on the shoulders of giants. This is only possible thanks to the great work already being done by Harmony, Johnson and SpiderMonkey. I’m hoping that other people will become inspired by the possibilities and take this further. Watch this space for more news!

Advertisements

Presentation of my apprenticeship task

Here is a presentation i made to some of my colleagues on friday explaining my first apprenticeship task, my wiki, how i went about it, the problems i encountered and what i learnt from the whole experience.

See my Apprenticeship task presentation on youtube.

A testing framework

Enrique has amended my task so that i cannot use any existing testing framework, not even Ruby’s built-in Test::Unit. However, i must practice test-driven development! Ha ha ha!

So here i am trying to ponder how to write a unit testing framework. Which is actually really nice because i get to decide the syntax i would like to use, and maybe – just maybe – i’ll come up with something that suits me really well and i’ll use again.

Enrique pointed me to the Given specification framework which i like because it uses the BDD ‘given/when/then’ approach for unit-level specifications. I’m already used to this kind of format for Cucumber features. Effectively, we use ‘given/then/when’ for Rspec, but it’s not made explicitly obvious. My comments in this trivial example should make it obvious:

describe "example" do
  before do
    @counter = 1             #(given)
  end

  it "increments the counter" do
    @counter += 2            #(when)
    @counter.should == 3     #(then)
  end
end

Now i think i could make the ‘given/when/then’ more obvious, and i can also simplify it. I might actually change ‘then’ to ‘expect’, meaning i can do away with the ‘should’ method. Effectively i’m saying that whatever is in the ‘expect’ block should return true, otherwise the spec fails. Here’s what i’m thinking of:

spec do
  given { @counter = 1 }
  when { @counter += 2 }
  expect { @counter == 3 }
end

I suppose this is what people call a DSL – a domain-specific language. That is the kind of language i’d like to use to write my specs. I’ve never written a DSL before, but i’m excited by the idea of being able to write specs like this, so i’m looking forward to giving it a go!

A little voice in the back of my head is telling me i’m going to have to finally understand ‘lambda’ properly. Lambda is one of those things that never sticks in my head. I can look it up when i need to and blunder my way to making it work, but i never really understand what it’s doing or how or why. I have a suspicion that i’ll be making extensive use of ‘lambda’ in my testing framework! :)