RSpec-Rails Extensions

Some prettifications for RSpec Rails.

Installation

sudo gem install rspec-rails-ext

Then, within your spec/spec_helper.rb

require 'rspec_rails_extensions'

ActiveRecord::RecordInvalid

If you try to call ActiveRecord::RecordInvalid.new(@my_object) where @my_object is a mock then your exception will not be raised; instead you get a cryptic error. This is because RecordInvalid expects to be able to call certain methods on your ActiveRecord model, which your mock isn’t set up for. So instead, prepare your mock, like so:

@thingy = mock_model Thingy, :field => 'value'
prepare_for_errors_on @thingy
raise ActiveRecord::RecordInvalid.new(@thingy)

There’s also a short-cut method:

@thingy = invalid_model Thingy, :field => 'value'
raise ActiveRecord::RecordInvalid.new(@thingy)

Matchers

Some helpful matchers for testing HTTP statuses beyond the default be_success (200 OK)

# looks for a 201 Created (useful for API CREATE calls)
response.should be_successfully_created 

# expects a Location field with the given URL (useful for API CREATE calls)
response.should point_to(url) 

# looks for a 422 response (invalid object)
response.should be_unprocessable

# looks for a 404
response.should be_not_found

# looks for a 401
response.should be_unauthorised

# looks for a 500
response.should be_an_error

Also a helper for testing which layout was used

# Makes sure it used the "bare" layout
response.should use_layout("bare")

Specifying your controllers

One of the issues with writing standard controller specifications, using RSpec’s inbuilt mock objects is that the “flow” of your specs feels wrong.

it "should display a page for a given thingy" do
  @thingy = mock_model Thingy, :name => 'Wotsit'
  Thingy.should_receive(:find).with('1').and_return(@thingy)

  get :show, :id => '1'

  response.should be_success
  response.should render_template('thingies/show')
end

In other words you are setting expectations before you are doing the work - technically correct but it reads wrongly.

These extra helpers let you write specifications in a more english-like manner.

it "should display a page for a given thingy" do
  @thingy = mock_model Thingy

  on_getting :show, :id => '1' do
    Thingy.should_receive(:find).with('1').and_return(@thingy)
  end

  response.should be_success
  response.should render_template('thingies/show')
end

Isn’t that lovely?

ComparableArray

Ever wanted to check an array contains the right elements, but not cared about the order they were returned? Enter ComparableArray!

ComparableArray.new([:foo, :bar]).should =~ [:bar, :foo]

And there’s even a shorthand for it

(~[:foo, :bar]).should =~ [:bar, :foo]

It’s not required by default, you need to explicity require it.

require "rspec_rails_extensions/comparable_array"