"Ramble", the Javascript Cucumber Port (work in progress)

I am a great fan of Cucumber when it comes to integration testing, however testing heavy use of Javascript can be a little tedious.
I have looked into the different solutions out there such as Selenium but found them all to be fiddly to setup, however Capybara helps on this front. I was thinking, what if Cucumber could run in the browser? No need for Javascript adapters or XML parsers, Safari/Chrome/Firefox already do a great job of this. Manipulating the page such as filling in forms, clicking links etc. could all be done with jQuery, in a very concise manor.
I decided to create a proof-of-concept while it was still fresh in my head. This is by no means a fully working release and the code leaves a lot to be desired in it’s current state, however it shows the benefits of a “Cucumber in the browser”. The main benefits I can see so far (for both javascript and non javascript apps) are:

  • Speed – browsers are getting extremely quick at this DOM stuff.
  • Flexibility – everything happens client-side meaning you can easily test with any server technology.
  • Simplicity – no need for complex javascript adapters, XML parsers etc.

The basic file structure is very similar to Cucumber:

  - features
    - js
    - steps
    - support

Step definitions can be defined in plain old javascript files with plain old jQuery, in this case web-steps.js. Currently the step definition are expected to throw an error if they cannot be fulfilled, this may change when a solid API is nailed down:

  // The value of 'this' is the current document as a jQuery object.
  ramble.match(/^I follow "(.+)"$/, function(link_text) {
    var link = this.find('a').filter(function() { return $(this).text() == link_text; });
    if(!link.length) throw("Can't find link: " + link_text);

Scenarios are exactly the same as in Cucumber, so you can do something like the following:

  Scenario: User fill out a form
    Given I am on the homepage
    And I follow "Tell us your name"
    And I fill in "First name" with "Jamie"
    And I fill in "Last name" with "Hill"
    And I press "Submit"
    Then I should see "Thank you for your details."

You can now simply drop the features folder into the public area of your app and visit the url in your browser. You should see the relevant steps go green or red as the app is navigated in an iFrame.
I plan to first tidy up the API (and unit test) and then aid the writing of scenarios by adding the ability to record them within the browser (Selenium style).
I’d like to hear peoples views on this… please don’t be too hard on the code, it’s more of a mind-dump than anything else at this stage (around 100 lines). There is an example of testing a static site included so just load the features/index.html file in your browser to see it run.
Update 04/07/10: As noted by Andrew in the comments, you will need a server running in order for browsers to get access to the pages for testing.
I have added a simple server script allowing the features to be run locally (requires Ruby). If you want to see it in action, just run:

cd /path/to/ramble/checkout
ruby server.rb

…and then visit http://localhost:1234/features in your browser (tested in Firefox, Chrome and Safari). Note that Ramble is not at all dependent on Ruby, it is just used for running a local test server.


Ramble in action

Nokogiri for Cucumber steps that need to compare HTML attributes

I have found on numerous occasions that I need to look at HTML attributes when diffing tables etc. in Cucumber. My current need for this arrises as I have a gallery of thumbnails that all have alt tags who’s values I need to compare as there is not a text equivalent.
A feature statement like this:

Then I should see the following thumbnails
  | Mona Lisa  |
  | Sunflowers |

Could be achieved with the following step:

Then /^I should see the following thumbnails$/ do |table|
  nodes = Nokogiri::HTML(response.body).css('ul#gallery li img')
  table.diff!(nodes.map { |img| [img.attributes['alt'].to_s] })

Why I stopped using Pickle with Cucumber

…no, not because it leaves a bitter aftertaste, I’m talking about the Pickle step definitions for Cucumber.
I have lately been using Pickle when writing Cucumber features, however I have come to the conclusion that this is a bad idea. The reason being that when using Pickle, you create entries directly, whereas the whole point of Cucumber is that it is for high level integration testing.
What I do now, is to create any entries by filling out and submitting the relevant forms with a step definition, for example I may have the following:

Given an admin has created the following products
  | Name    | Variants                                  | Featured |
  | T-Shirt | Small: 10.99, Medium: 12.99, Large: 14.99 | Yes      |
  | Keyring | Default: 2.99                             | Yes      |

I would then write a step definition for this that would log in as the admin user, break this table apart and fill in the relevant forms.
I would go so far as to say that using factories at all in features is a bad idea and instead everything should happen via the user interface for better coverage. For any data that is known to exist when the app is deployed via ‘rake db:seed’, this can be loaded in the ‘env.rb’ file e.g.

load 'seeds.rb'

Update 11/2/2010: Sometimes this is simply not practical due to slowdown which is a shame, as noted by Amos in the comments.

Devise Rails Authentication Gem Rocks!

I’ve had a set of rather bespoke requirements for authentication on a recent project and thought I’d give Devise a go.
Devise uses Warden, which is an authentication solution build on Rack making it extremely flexible and usable across multiple frameworks e.g. Rails/Sinatra. Also, Devise is extremely modular meaning to can easily write custom “strategies” for specific behaviour.
I have used Clearance in the past which is great if you want an engine that will just work. Devise however is by far the most flexible and extensible solution that I have come across with the same ease of use as Clearance. The only thing that you don’t get with devise that you do with Clearance is the signup stage, however as this is normally custom on a per-app basis I can live with this.
One more thing to note is that Devise lets you have multiple auth systems in play e.g. one for users and one for admins.

Sexy Validation in Edge Rails (Rails 3)

I have just had my sexy validations patch accepted into Rails. Much thanks to José Valim for helping me get this applied.
The reason for the name “sexy validations” is that it gives a much more concise way of defining validation and reusing custom validator classes. Much like what sexy migrations did for defining your database schema.
Simple example of using existing Rails validations, the “sexy” way:

class Film < ActiveRecord::Base
  validates :title, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
  validates :budget, :presence => true, :length => { :within => 1..10000000 }

The power of the “validates” method comes though, when using in conjunction with custom validators:

class IntenseFilmTitleValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << "must start with 'The'" unless value =~ /^The/
class SpendValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    spend = case options[:size]
      when :big then 100000000
      when :small then 100000
    record.errors[attribute] << "must not exceed #{spend}" if value > spend
class Film < ActiveRecord::Base
  validates :title, :presence => true, :intense_film_title => true
  validates :budget, :spend => { :size => :big } # using custom options

All validations in Rails, along with other common model functionality have been extracted into ActiveModel, so you can also use validations and Validator classes without ActiveRecord e.g.

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << (options[:message] || "is not an email") unless
      value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
class Person
  include ActiveModel::Validations
  attr_accessor :name, :email
  validates :name, :presence => true, :length => { :maximum => 100 }
  validates :email, :presence => true, :email => true

Have fun!

Sexy Validations in Rails 3.0

Update: This patch has been applied but this information is out of date, please see: this new post.
Having had a patch accepted in Rails, I now have the bug (not a bug… the bug for writing patches… anyway).
I have just added this patch to allow for “sexy-migration-esque” validations using the new ActiveModel. Below explains the gist of it:

  class EmailValidator < ActiveRecord::Validator
    def validate
      field = options[:attr]
      record.errors[field] << "is not valid"
        unless record.send(field) =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
  # Register the custom validator in an initializer file.
  ActiveModel::Validations.register_validator :email, EmailValidator
  class Person < ActiveModel::Base
    attr_accessor :name, :email
    validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
    validates :email, :presence => true, :email => true

As with the previous patch I believe this adds even more flexibility which is what Rails 3 is all about. It also allows validators to be shared more easily.
Rails defaults can now also be overridden e.g.

  class RequiredValidator < ActiveRecord::Validator
    def validate
      field = options[:attr]
      record.errors[field] << "Required" if record.send(field).blank?
  ActiveModel::Validations.register_validator :email, RequiredValidator

I would appreciate a +1 on the ticket from anyone who feels this should be in Rails 3.0.

Unit testing by simplifying the problem: memoization

I see unit testing as a way to test each possible snippet of functionality and route the code in question can take. With Ruby being such a dynamic language and allowing shortcuts to common problems, sometimes it can seem somewhat of a mystery, how to test these snippets of functionality.
Using Memoization as an example:

class MyClass
  def lazy_initialized_value
    @lazy_initialized_value ||= Expensive.request

There are actually 3 separate snippets of functionality that need testing here, however it is not immediately obvious from the example. Lets be slightly more verbose about what is actually happening:

class MyClass
  def lazy_initialized_value
    @lazy_initialized_value = Expensive.request unless @lazy_initialized_value

Now it is much easier to see the 3 steps the code should take:
* Store result of expensive request in instance variable
* Leave instance variable alone when it is already set
* Return the value of the instance variable
Now we have this information, our tests become (using Mocha to mock external methods):

class Expensive; end
module Tests::MyClass
  # lazy_initialized_value
  # ----------------------
  class LazyInitializedValueTest < Test::Unit::TestCase
    def test_should_respond
      assert_respond_to MyClass.new, :lazy_initialized_value
    def test_should_store_result_of_expensive_request_in_instance_variable
      instance = MyClass.new
      Expensive.stubs(:request).with().returns('expensive value')
      assert_equal 'expensive value', instance.instance_variable_get('@lazy_initialized_value')
    def test_should_return_value_of_instance_varable
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'the value'
      assert_equal 'the value', instance.lazy_initialized_value
    def test_should_maintain_existing_instance_variable_value_when_already_set
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'existing value'
      assert_equal 'existing value', instance.instance_variable_get('@lazy_initialized_value')

Now we have these tests in place, we can go back and refractor the code ’til our heart’s content using all the tricks in the book but by simplifying the problem in the first place, it gives us a solid test suite and the confidence to make changes without breaking functionality.
If you were solving this problem test-first then you wouldn’t (but more likely, shouldn’t) have written the first example until re-factoring stage anyway, however when these shortcuts become engrained in your brain, it’s all too easy to forget what they are _actually_ doing.
So there we go, simplify the initial implementation, get a solid test suite in order, _then_ re-factor.

Rails: Using Autotest with UnitRecord

Myself and a colleague have just managed to waste away a good couple of hours trying to figure out Autotests strange ‘style’ mechanism to add the ability to “test in the way Jay Fields explains”:http://blog.jayfields.com/2007/09/rails-how-we-test.html using “UnitRecord”:http://unit-test-ar.rubyforge.org/.
You can grab our plugin to enable “UnitRecord”:http://unit-test-ar.rubyforge.org/ when using “Autotest”:http://rubyforge.org/projects/zentest below:
By default, running autotest in the Rails directory will run the unit tests. To run the functional tests, do: AUTOTEST='functional' autotest
I hope this saves some people some time!!

SonicIQ Hiring! – UK, Ruby on Rails Developer Required

We are looking for a Ruby on Rails, XHTML & CSS Developer to join our team at “SonicIQ”:http://soniciq.com. Head over to “43folders job board to view our ad”:http://jobs.43folders.com/job/6a5255713e8351a5eb2efef7805b7629/?d=1.
These are exiting times with projects like “Propel’r”:http://propelr.com in the pipeline, along with the ever-growing opportunities for new and interesting client projects.
If you are a highly motivated developer and can see yourself in a Ruby on Rails position in sunny (sometimes) Bournemouth, UK then “apply at 43folders”:http://jobs.43folders.com/job/6a5255713e8351a5eb2efef7805b7629/?d=1.

Using Rcov to measure the test coverage of Rails plugins

To view the coverage of your plugins using Rcov, first install the rcov gem with sudo gem install rcov, then copy and paste the following onto the end of the Rakefile inside your plugin directory:

require 'rcov/rcovtask'
desc 'Measures test coverage using rcov'
namespace :rcov do
  desc 'Output unit test coverage of plugin.'
  Rcov::RcovTask.new(:unit) do |rcov|
    rcov.pattern    = 'test/unit/**/*_test.rb'
    rcov.output_dir = 'rcov'
    rcov.verbose    = true
  desc 'Output functional test coverage of plugin.'
  Rcov::RcovTask.new(:functional) do |rcov|
    rcov.pattern    = 'test/functional/**/*_test.rb'
    rcov.output_dir = 'rcov'
    rcov.verbose    = true

You can now simply run rake rcov from inside your plugin directory which will generate an rcov directory with the results. Open rcov/index.html (if you are on OSX this will open automatically) in a browser to view the results.
Thanks to “Mike Clark”:http://clarkware.com/cgi/blosxom for his “Rcov rake task for Rails”:http://clarkware.com/cgi/blosxom/2007/01/05#RcovRakeTask which this task is based on.
*Update (11-11-08):* Changed code to use the RcovTask class.