Structure of RSpec tests

Disclaimer

Overview

A typical test

describe Classroom do
  context "#a_mess?" do
    it "should return true after a party" do
      classroom = Classroom.new
      classroom.throw_pizza_party
      classroom.a_mess?.should be_true
    end
  end
end

describe vs. context

describe vs. context

describe vs. context

Things

Bad:
context "#matriculate"
Better:
describe "#matriculate"

describe vs. context

States

Bad:
describe "when the student is sick"
Better:
context "when the student is sick"

describe vs. context

Prefer context

context naming

context naming

Bad:
describe "#assign"
Better:
context "assigning homework to a student"

before vs. let

before vs. let

before vs. let

Actions

Bad:
let(:dummy) do
  @classroom.initialize_roster
end
Better:
before do
  @classroom.initialize_roster
end
Note: let is lazy; watch out for let!.

before vs. let

Dependencies

Bad:
before { @grade_levels = [1, 2, 3] }
Better:
let(:grade_levels) { [1, 2, 3] }

before aligns with the context

before & context

For real objects

context "the bathroom queue is full" do
  before do
    100.times { bathroom_queue << nil }
  end
end

before & context

For mock objects

context "the bathroom queue is full" do
  before do
    bathroom_queue.stub(:full?).and_return(true)
  end
end

let vs. subject

let vs. subject

before vs. let

Bad:
let(:classroom) { Classroom.new(grade: 5) }
Better:
subject(:classroom) { Classroom.new(grade: 5) }
Note: If your constructor takes no parameters, there's no need to define subject, but giving it a name is still a good idea.

subject aligns with the describe

subject & describe

At the top level

describe ParentNotifier do
  subject(:notifier) do
    ParentNotifier.new(phone: '1.555.555.5555')
  end
end

subject & describe

At a nested level

describe ParentNotifier do
  subject(:notifier) do
    ParentNotifier.new(phone: '1.555.555.5555')
  end

  describe "the result of calling the parent" do
    subject(:call_result) { notifier.call }
  end
end

Remove weak words

Remove weak words

Bad:
it "should add the student to the class"
Better:
it "adds the student to the class"

One assertion per test

One assertion per test

Bad:
it "..." do
  homework.should be_available
  homework.should_not be_expired
end
Better:
it "..." { homework.should be_available }
it "..." { homework.should_not be_expired }
Note: You could use streamlined it blocks here.

One assertion per test

Bad:
before do
  school_board.should_receive(:funding)
              .and_return(100_000)
end
it { should be_funded }
Better:
before do
  school_board.stub(:funding)
              .and_return(100_000)
end
it { should be_funded }

One assertion per test

Bad:
it "..." do
  lunch.size.should eql(3)
  lunch[1].should be_salted
end
Better:
it { should include_fries }
RSpec::Matchers.define :include_fries
  match do |actual|
    actual.size == 3 && actual[1].salted?
  end
end

A typical test

describe Classroom do
  context "#a_mess?" do
    it "should return true after a party" do
      classroom = Classroom.new
      classroom.throw_pizza_party
      classroom.a_mess?.should be_true
    end
  end
end

A better typical test

describe Classroom do
  subject(:classroom) { Classroom.new }
  context "when the children are rowdy" do
    before { classroom.throw_pizza_party }
    it { should be_a_mess }
  end
end

So what's it all good for?

Documentation format

Bad:
Classroom
  #a_mess?
    should return true after a party
Better:
Classroom
  when the children are rowdy
    should be a mess

Things I learned

Obligatory self-promotion

Want more?

/

#