Generic Fixture Selection

I have done a lot of generic test writing in the last months, and one thing that always resurfaced was:

item = send(@obj.class.to_s.underscore.pluralize,:one)
item = send(@item_name.to_s.pluralize.:one)
...

What does it do ? It load the fixture :one for this objects class, since i always keep a fixture :one and :two around to make generic tests less painful. But all this send madness was unreadable, so i factored out some fixture calling.

#spec/spec_helper.rb OR test/test_helper.rb

#helper for generic fixture calling
#fixture 'user','one' = users('one')
#fixture @user,'one' = users('one')
#fixture User,'one' = users('one')
def fixture(singular,name)
  klas = input_to_class(singular)
  fixture = find_fixture(klas,name)
  raise "fixtures for #{klas} not loaded!" unless fixture
  fixture
end

def find_fixture(klas,name)
  table = klas.to_s.underscore.pluralize
  return send(table,name) if respond_to? table
  false
end

def input_to_class(val)
  return val if val.kind_of? Class
  return val.class if val.kind_of? ActiveRecord::Base
  return val.to_s.classify.constantize
end

Include all Helpers for Testing

When your helpers use each other, or your view tests use many, it gets frustrating to include them by hand…
This will load all helpers from app/helpers dir and include them!
Happy, stress-free testing!

def include_helpers
  Dir.glob("#{RAILS_ROOT}/app/helpers/*_helper.rb").each do |file|
    helper = File.basename(file).sub('.rb','').camelcase.constantize
    include helper
  end
end

Your Controller/View/Helper test would look like this:

describe MoviesHelper do
  include_helpers
  ...
end

Getting all other helpers(e.g. from helpers/admin is more complicated, but surely possible…)

Tests should be Documentation with Examples

Since no one wants to write the documentation, the simplest solution would be to let everyone write documentation, without noticing…

Normal documentation:

An Organisation is a firm or school that has an 
address(required) and can have users as members.

Pragmatig documentation with examples:

Organisation:
- requires an address
  organisation.address
- can have users as members
  organisation.members << User

Test:

describe Organisation do
  it "requires an address" do
    @organisation.should_not be_valid?
    @organisation.address = Address.new
    @organisation.should be_valid
  end

  it "can have users as members" do
    @organisation.should be_valid
    @organisation.members << User.new
    @organisation.should be_valid
  end
end
#before :each omitted

With the help of some spec -f or agiledox magic, we can extract:

An Organisation:
- requires an address
- can have users as members

looks familliar ?

I prefer this kind of testing, since it does not generate so many 3-liners(2(do/end)+1) nor uses “it should” all the time, which makes it look more documentation-ish.

DISCLAIMER: This way of writing tests is not RSpec-pure in that it does not use ‘it should’ and has more than on assertion (should) per example.

RSpec response.should Information Enhancers

In case of failure the normal should be_redirect and its friends are far from helpful…

expected redirect? to return true, got false

expected "new", got nil

Now you can have this:

Status should be redirect but was 200(success)
 - rendered addresses/new
 - Flash:
    :error = Address contains errors!
 - Errors:Errors on @address(Address):
     City can't be blank

Usage

  • response.should redirect_to / render_template as normal
  • response.should have_been_success (i do not want to overwrite be_success…)
  • have_been_error
  • have_been_missing
  • have_been_redirect

Install

script/plugin install git://github.com/grosser/rspec_response_enhancer.git

add to spec/spec_helper.rb:
Spec::Runner.configure do |config|
  ...
  config.include(RspecResponseEnhancer)
  ...
end

And as added benefit you no longer need to build should be_success when you already have a should render_template, since render_template now gives detailed error messages!

Fighting the Spec Bloat

Every time i run rspec_scaffold and look at the generated specs im shocked anew, there is just so much bla bla and so little information. I know that as a rule of thumb there should be one assertion per test, but in my opinion this is going too far. Note to self: New rule of thumb: “Test one aspect per test”. Meaning that i test ‘database interaction’ and ‘output’ (renders x + assigns y + flash z) in different tests. And when there is only simple logic (1-liner) in either of them, they may be merged.

OLD

before(:each) do
  @address = mock_model(Address)
  Address.stub!(:find).and_return([@address])
end

def do_get
  get :index
end

it "should be successful" do
  do_get
  response.should be_success
end

it "should render index template" do
  do_get
  response.should render_template('index')
end

it "should find all addresses" do
  Address.should_receive(:find).with(:all).and_return([@address])
  do_get
end

it "should assign the found addresses for the view" do
  do_get
  assigns[:addresses].should == [@address]
end

NEW

it "should render index and assign all addresses" do
  Address.expects(:find).with(:all).once.returns([@address])
  
  get :index
  
  response.should render_template('index')
  assigns[:addresses].should equal([@address])
end