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 }
end

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/
  end
end

class SpendValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    spend = case options[:size]
      when :big then 100000000
      when :small then 100000
    end
    record.errors[attribute] << "must not exceed #{spend}" if value > spend
  end
end

class Film < ActiveRecord::Base
  validates :title, :presence => true, :intense_film_title => true
  validates :budget, :spend => { :size => :big } # using custom options
end

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
  end
end

class Person
  include ActiveModel::Validations
  attr_accessor :name, :email

  validates :name, :presence => true, :length => { :maximum => 100 }
  validates :email, :presence => true, :email => true
end

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
    end
  end
  
  # 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
  end

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?
    end
  end

  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
  end
end

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
    @lazy_initialized_value
  end
end

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
    end
  
    def test_should_store_result_of_expensive_request_in_instance_variable
      instance = MyClass.new
      Expensive.stubs(:request).with().returns('expensive value')
      instance.lazy_initialized_value
      assert_equal 'expensive value', instance.instance_variable_get('@lazy_initialized_value')
    end
    
    def test_should_return_value_of_instance_varable
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'the value'
      Expensive.stubs(:request)
      assert_equal 'the value', instance.lazy_initialized_value
    end
    
    def test_should_maintain_existing_instance_variable_value_when_already_set
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'existing value'
      Expensive.stubs(:request)
      instance.lazy_initialized_value
      assert_equal 'existing value', instance.instance_variable_get('@lazy_initialized_value')
    end
  end
end

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:

“http://svn.soniciq.com/public/rails/plugins/iq_autotest”:http://svn.soniciq.com/public/rails/plugins/iq_autotest

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
  end
  
  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
  end
end

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.

Autotest Growl Fail/Pass Smilies

John Nunemaker posted a handy tip on “setting up autotest to work with Growl”:http://railstips.org/2007/7/23/autotest-growl-pass-fail-notifications

I use this all the time now however I didn’t like the ugly smilies (call me shallow if you like). I used “Wolfgang Bartelme’s”:http://bartelme.at “Smily Devkit”:http://bartelme.at/journal/archive/smiley_devkit to make a couple of PNG’s slightly more pleasing to the eye.

p=. !http://thelucid.com/files/fail.png(Autotest Fail image)!
!http://thelucid.com/files/pending.png(Autotest Pending image)!
!http://thelucid.com/files/pass.png(Autotest Pass image)!

The zip file can be downloaded here: “autotest_images.zip”:http://thelucid.com/files/autotest_images.zip

*Update* 17-08-07: Added ‘pending’ image for RSpec as requested by Aslak Hellesoy