Hack hack hack...

An open journal-- some of it written for you, but most of it is for me.

RSpec

1
2
3
4
5
6
it "has no brains" do
  zombie = Zombie.new
  #modifier is should
  #matcher is the less than sign
  zombie.brains.should < 1
end
Matchers and Modifiers
1
2
3
4
5
6
7
zombie.name.should == 'Ash'
zombie.alive.should == false
zombie.rotting.should == true
zombie.height.should > 5
zombie.height.should >= 5
zombie.height.should < 5
zombie.height.should_not == 5
Predicate ‘be’
1
2
3
4
5
6
7
8
describe Tweet do
  it 'without a leading @ symbol should be public' do
    tweet = Tweet.new(status: 'Nom nom nom')
    tweet.status.should be_true
    #this is equivalent to tweet.status.should == true
    #the predicate is a better way though
  end
end
Mark as Pending
1
2
3
4
5
6
7
8
9
10
it "is named Ash"
end

#to mark it as pending
xit "is named Ash" do
end
#OR
it "is named Ash" do
  pending
end
Should be
1
2
3
4
5
6
7
describe Tweet do
  it 'truncates the status to 140 characters' do
    tweet = Tweet.new(status: 'Nom nom nom' * 100)
    # notice the space between the 'should' and 'be'
    tweet.status.length.should be <= 140
  end
end

Level 2

Installing RSpec

Should be
1
2
3
4
5
gem install rspec
rspec --init

#in rails
rails generate rspec:install

Running RSpec:

Should be
1
2
3
4
5
6
7
rspec
#to run a directory
rspec spec/models/
#to run a specific test
rspec spec/models/zombie_spec.rb
#to run a specific line
rspec spec/models/zombie_spec.rb:4

Should match: zombie.name.should matchj(/Ash Clone \d/)

Include should be used anywhere where an item should be part of an array…

Matchers: include
1
2
3
4
5
6
7
describe Zombie do
  it 'includes a tweet' do
    tweet = Tweet.new
    zombie = Zombie.new(tweets: [tweet])
    zombie.tweets.should include(tweet)
  end
end
Matchers: have
1
2
3
4
5
6
7
8
9
10
11
12
#this
zombie.weapons.count.should == 2
#is better written as
zombie.should have(2).weapons

describe Zombie do
  it 'increases the number of tweets' do
    zombie = Zombie.new(name: 'Ash')
    zombie.tweets.new(message: "Arrrgggggggghhhhh")
    zombie.should have(1).tweets
  end
end
Can also have
1
2
3
have(n)
have_at_least(n)
have_at_most(n)
Expect Block and Change Methods
1
2
3
4
5
6
describe Zombie do
  it 'gains 3 IQ points by eating brains' do
    zombie = Zombie.new
    expect { zombie.eat_brains }.to change { zombie.iq }.by(3)
  end
end
Can also use
1
2
3
4
5
.by(n)
.from(n)
.to(n)
#and they can be chained
.from(1).to(5)
should have
1
2
3
4
5
6
7
describe Zombie do
  it 'increases the number of tweets' do
    zombie = Zombie.new(name: 'Ash')
    zombie.tweets.new(message: "Arrrgggggggghhhhh")
    zombie.should have(1).tweets
  end
end
Raise_error
1
2
3
4
5
6
7
8
9
10
describe Zombie do
  it 'raises a Zombie::NotSmartEnoughError if not able to make a decision' do
    zombie = Zombie.new
    expect { zombie.make_decision! }.to raise_error(StandardError)
  end
end
#these modifiers also work
to
not_to
to_not

More Matchers

A list
1
2
3
4
5
6
@zombie.should respond_to(hungry?)
@zombie.should be_within(0.1).of(33.3)
@zombie.should exist
@zombie.should satisfy { |zombie| zombie.hungry? }
@hungry_zombie.should be_kind_of(Zombie) #Inheritance: HungryZombie < Zombie
@status.should be_an_instance_of(String)

Drying up specs

Subject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#this
describe Zombie do
  it 'should not be a genius' do
    zombie = Zombie.new
    zombie.should_not be_genius
  end
end
#can be simplified to
describe Zombie do
  it 'should not be a genius' do
    subject.should_not be_genius
  end
end
#this can be further simplified to
describe Zombie do
  it 'should not be a genius' do
  #implicit subject
    should_not be_genius
  end
end
#OR
describe Zombie do
  it {should_not be_genius}
end

This is only going to work if using a describe block with a class in it…

it
1
2
3
describe Zombie do
  it { subject.name.should == 'Ash' }
end
its
1
2
3
4
5
6
7
8
9
describe Zombie do
  it 'should have an iq of zero' do
    subject.iq.should == 0
  end
end
#can simplify it all the way to
describe Zombie do
  its(:iq) { should == 0 }
end
its examples
1
2
3
4
5
6
describe Zombie do
  its(:name) { should == 'Ash' }
  its(:weapons) { should include(weapon) }
  its(:brain) { should be_nil }
  its('tweet.size') { should == 2 }
end
Refactoring with Context
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
describe Zombie do
  it { should_not be_genius }
  its(:iq) { should == 0 }

  it "should be_genius with high iq" do
    zombie = Zombie.new(iq: 3)
    zombie.should be_genius
  end

  it 'should have a brains_eaten_count of 1 with high iq' do
    zombie = Zombie.new(iq: 3)
    zombie.brains_eaten_count.should == 1
  end
end
#Refactored with context and subject and its
describe Zombie do
  it { should_not be_genius }
  its(:iq) { should == 0 }

  context "with high iq" do
    subject { Zombie.new(iq: 3) }
    it { should be_genius }
    its(:brains_eaten_count) { should == 1 }
  end
end

Use the let keyword to create different objects since you can only use subject once.

Let
1
2
3
4
5
6
7
8
9
describe Zombie do
  let(:tweet) { Tweet.new }
      subject { Zombie.new(tweets: [tweet], name: [zombie]) }
      #moved zombie to its own let
      let(:zombie) { Zombie.new}

  its(:tweets) { should include(tweet) }
  its(:latest_tweet) { should == tweet }
end
Let! instantiates the zombie everytime instead of lazy eval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe Zombie do
  context "with high iq" do
  #changed Zombie.new to Zombie.create
     let!(:zombie) { Zombie.create(iq: 3, name: 'Anna') }
     subject { zombie }

     it "should be returned with genius" do
       Zombie.genius.should include(zombie)
     end

     it "should have a genius count of 1" do
       Zombie.genius.count.should == 1
     end
  end
end

Using let with FactoryGirl

1
2
   let(:issue) { create(:issue) }
   let(:user) { create(:user) }

Hooks

1
2
3
4
before(:each)
before(:all)
after(:each)
aftter(:all)
1
2
3
4
5
6
7
8
describe Zombie do
  let(:zombie) { Zombie.create }
  subject { zombie }
  before { zombie.eat_brains }

    it { subject.should_not be_dummy}
    it { subject.should be_genius }
end
1
2
before { zombie.hungry! }
#by default, a before clock runs each time before an example is about to run
Before Hook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
describe Zombie do
  let(:zombie) { Zombie.create }
  subject { zombie }

  it 'is not a dummy zombie' do
    zombie.eat_brains
    zombie.should_not be_dummy
  end

  it 'is a genius zombie' do
    zombie.eat_brains
    zombie.should be_genius
  end
end
# refactored as
describe Zombie do
  let(:zombie) { Zombie.create }
  subject { zombie }
  before { zombie.eat_brains }

  it 'is not a dummy zombie' do
    zombie.should_not be_dummy
  end

  it 'is a genius zombie' do
    zombie.should be_genius
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe Zombie do
  let(:zombie) { Zombie.create }
  before { zombie.iq = 0 }
  subject { zombie }

  context 'with a dummy zombie' do
    before { zombie.iq < 3 }
    it { should be_dummy }
  end

  context 'with a smart zombie' do
    before { zombie.iq = 3 }
    it { should_not be_dummy }
  end
end

Shared Examples

Shared Examples
1
2
3
4
5
6
7
8
9
#used to call shared examples
it_behaves_like 'yada yada yada'

#refers to the implicit subject
shared_examples_for 'the undead' do
  it 'does not have a pulse' do
      subject.pulse.should == false
  end
end
for a more explicit subject
1
2
3
4
5
6
7
8
9
10
11
12
13
describe Zombie do
#this is clearer
  it_behaves_like 'the undead' do
    let(:undead) {Zombie.new}
  end
#can do this
  it_behaves_like 'the undead', Zombie.new

shared_examples_for 'the undead' do |undead|
  it 'does not have a pulse' do
    undead.pulse.should == false
  end
end

Comments