Peter Marklund

Peter Marklund's Home

Wed May 23 2007 02:54:35 GMT+0000 (Coordinated Universal Time)

Rails + RSpec: First Impressions

I started using RSpec on a client project yesterday and I've been pleasantly surprised by how easy it is to get started with and how nicely it plays with Rails. I now have a handful of specs under the spec directory and they get run by rake automatically right after my tests. I can also use rake spec:doc to generate the documentation of my specs:

$ rake spec:doc

Admin::StatisticsController
- Index page is not viewable by role user or role admin without service id
- Index page, role user: shows links
- Index page, role super: shows links
- Hours page, role user: does not show customer and service selects
- Hours page, role super: selects service when service provided
- Hours page, role super: selects service when service and customer provided
- Hours page, role super: selects customer when customer provided

the Call model
- Is not deleted by attempt to delete customer
- Is not deleted by attempt to delete service

the Service model
- Is not deleted by attempt to delete customer

I decided to not prefix all my specifications with the "should" keyword, it seemed superfluous. Here are some samples from my controller spec:

require File.join(File.dirname(__FILE__), "/../../spec_helper")

describe Admin::StatisticsController do
  fixtures :customers, :services, :users, :services_users, :audio_files, :prompts, :calls  
  integrate_views

  specify "Index page is not viewable by role user or role admin without service id" do
    login(:user)
    get :index
    response.should be_error

    login(:admin)
    get :index
    response.should be_error
  end
  
  specify "Index page, role user: shows links" do
    login(:user)
    get :index, :id => 2
    response.should be_success
    response.should have_tag("a[href=/admin/statistics/hours/2]")
    response.should have_tag("a[href=/admin/statistics/calls/2]")
  end
  ...

  ######################################################
  #
  # Helper methods
  #
  ######################################################

  def login(role)
    login_user(user_for_role(role))
  end

  def user_for_role(role)
    case role
    when :super
      users(:aaron)
    when :admin
      users(:admin)
    when :user
      users(:customer)
    else
      raise "Unknown role #{role}"
    end
  end
  
  def login_user(user)
    @controller.send(:session=, ActionController::TestSession.new)
    @controller.send(:current_user=, user)
  end
end

I think it's very refreshing to switch to RSpec from unit tests. The syntax is more readable and makes it feel natural to write specs/tests before the code. In this particular case though I wrote the spec first, but when I started implementing the UI I changed my mind about which page the form should go on, experimented with the UI a bit, and ended up writing the spec after the fact. I don't consider this a deadly sin. One probably shouldn't be too religious about writings specs before the code in cases where it slows you down too much or hinders your creativity.

I highly recommend you try out RSpec with Rails if you haven't already.