Notes posted to RSpec

RSS feed
April 16, 2014
3 thanks

Sample

Here is a sample usage of its,

Code example

describe '#rate_for_date' do
  context 'with date with in the range' do
    subject do 
      FactoryGirl.build(:allocated_room_rate, 
          start_date: Time.zone.now, 
          end_date: 2.day.from_now, 
          price: 1000)
    end
    its(:daily_rate) { should == 500 }
  end
end
December 9, 2013 - (1.3.0 - 1.3.1)
0 thanks

description for described_class

Here is an example to understand described_class

If while describing the rspec, we use a Class name in the describe part, we can use described_class method to refer to that and use it as a class in the specs within that description.

Example:

describe MyClass do
  it "is available as described_class" do
    expect(described_class).to eq(MyClass)
  end
end

So in the above example, described_class method will return MyClass as a class.

It is a part of rspec core 3.0

Reference: http://www.relishapp.com/rspec/rspec-core/docs/metadata/described-class

July 12, 2013 - (1.1.4 - 1.3.1)
1 thank

correct, but ..

stub_chain provides a very good replacement of long lines of nested stubs, but never forget it violates Law of Demeter; i.e. it indicates an increase of coupling in your classes and this is a bad thing because it means your objects now are making more unnecessary calls to other objects. for example:

def initialize(some_obj)
  @obj = some_obj
end

def foo
  @obj.x  # GOOD coupling - according to LoD you are allowed to call a method on your object
end

def bar
  @obj.x.y   # BAD coupling - can not call a method on a returned value of another method call even if the initial call is legal
end

How is this related to TDD and stubs?

  • method foo test will have only one stub for a double of some_obj type

  • method bar will have 2 stubs: the first is going to swallow the other one to produce the result (and then can be shortened using this stub_chain technique)

Always remember: if your tests are using stub_chains –> your code is smelly and possibly tightly coupled.

June 3, 2013 - (1.3.0 - 1.3.1)
1 thank

as_null_object working

It only listen for the messages we tell it to expect and ignore any other messages.

For example:

spec/codebreaker/game_spec.rb

module Codebreaker
 describe Game do
   describe "#start" do
     before(:each) do
       @output = double('output').as_null_object
       @game = Game.new(@output)
     end

     it "sends a welcome message" do
       @output.should_receive(:puts).with('Welcome to Codebreaker!')
       @game.start
     end
     it "prompts for the first guess" do
       @output.should_receive(:puts).with('Enter Guess:')
       @game.start
     end
   end
 end
end

In first example we are expecting ‘Welcone to Codebreaker!’ while in second example we expect ‘Enter Guess:’

and in before(:each) first line we are using as_null_object which allowing us to only check if expected string exists in game.start method then it will pass.

lib/codebreaker/game.rb

module Codebreaker
 class Game
   def initialize(output)
     @output = output
   end
   def start
     @output.puts 'Welcome to Codebreaker!'
     @output.puts 'Enter Guess:'
   end
 end
end
December 1, 2012
1 thank

This method is deprecated in rspec 2.0

  • be_close(1, 0.1) is deprecated.

  • please use be_within(0.1).of(1) instead.

August 14, 2011
1 thank

stub/stub! will always be followed by '.and_return'

this function will aways be followed by ‘.and_return(…)’ because a stub is typically used for returning values. The required argument given to stub is a method name. When a message to this stubbed method name is received by a class or existing object of the class AND ‘.and_return’ is provided, the stub will return whatever was provided as argument to ‘.and_return’.

For example,

HomeLoan.stub!(interest_rate).and_return(‘5.5%’)

  • this will return 5.5% when a message for interest_rate in a HomeLoan class’s object is received.

HomeLoan.stub!(interest_rate).and_return(‘5.5%’, ‘3%’)

  • this will return 5.5% when a message for interest_rate in a HomeLoan class’s object is received FOR THE FIRST TIME but will return 3% for subsequent calls/messages.

April 24, 2011
0 thanks

Input strings are treated as regexp

Input strings are treated as regexp, but you can escape special regexp characters as usual:

"*test*".should match "\\*test\\*" #=> pass
"*test*".should match '\*test\*' #=> pass
"*test*".should match /\*test\*/ #=> pass
April 22, 2011
0 thanks

Careful when comparing strings

Input String is treated as Regexp:

"*test*".should match "*test*" #=> fail
"*test*".should match ".*test.*" #=> pass

Regexp special characters inside input String can’t [be] escaped:

"*test*".should match "\*test\*" #=> fail
"*test*".should match /\*test*\/ #=> pass
April 22, 2011
0 thanks

Eql equals ==

Use eql to compare values as you would use ==:

"test".should eql "test" #=> pass
"test".should == "test" #=> pass

Do not confuse with equal which compares objects.

April 22, 2011
0 thanks

Test strings with eql

Equal fails when comparing two different string objects:

"test".should equal "test" #=> fail
"test".should match "test" #=> pass
"test".should eql "test" #=> pass

In fact:

"test".object_id.should_not eql "test".object_id #=> pass

Match fails when the string contains regex special characters not escaped:

"*test*".should match "*test*" #=> fail for invalid regex
"*test*".should eql "*test*" #=> pass

In fact, match treats input as regexp:

"*test*".should match /\*test\*/ #=> pass
March 31, 2011
0 thanks

Test strings with match

To test a string use match, e.g.

"test".should match("test")
July 21, 2010
2 thanks

Works only inside the "it" block

Please note that stub_chain doesn’t work outside of the it...do...end block.

If you need to create more complicated chains using a function you need to use the old way.

March 4, 2010 - (>= 1.2.8)
5 thanks

stub_chain is very useful when testing controller code

or any other chained method call type that you’d like to stub, example:

in your controller:

def new
  @user = current_site.users.new
end

in your spec:

it "#new should assign a @user" do 
  u = mock("User")
  controller.stub_chain(:current_site, :users, :new).and_return(u)
  assigns[:user].should == u
end

whereas before you had to stub each chained method call separately:

it "#new should assign a @user" do 
  u = mock("User")
  users = mock("Users collection", :new => u)
  site = mock("Site", :users => users)
  controller.stub!(:current_site).and_return(site)
  assigns[:user].should == u
end

Please note that stub_chain was added to RSpec in version 1.2.6

June 12, 2009
0 thanks

Reusing shared examples

share_examples_for “a shape” do

   it "should have a color" do
     @shape.color.should == :black
   end
 end

describe "a circle" do
  before(:all) do
    @shape = Circle.new
  end 
  it_should_behave_like "a shape"
end
June 12, 2009
0 thanks

A stub with argument and return value

it “should use a dummy method with argument and return value” do

 dummy = mock("dummy").stub!(:emulate)
 dummy.should_receive(:emulate).with(:something).and_return("Done! sir!")
 dummy.emulate(:something).should == "Done! sir!"
end
June 4, 2009
2 thanks

A typical usage for a mock

You want to use a mock when you’re testing a behaviour of one of your methods that interacts with some outside world service (eg. an FTP server).

it "should login to ftp server" do
  ftp = mock('Ftp server', :null_object => true)
  Net::FTP.should_receive(:new).and_return(ftp)
  ftp.should_receive(:login).with('username', 'password')
  some_obj.connect
end

def connect
  session = Net::FTP.new('server.com')
  session.login('username', 'password')
  session.close
end
June 1, 2009
1 thank

Shared examples

Use it together with share_examples_for like this:

share_examples_for "a shape" do
  it "should have a color" do
    # ...
  end

  it "should have a center point" do
    # ...
  end
end

describe "a circle" do
  it_should_behave_like "a shape"

  it "should be round" do
    # ...
  end
end
June 1, 2009
0 thanks

Typical stub! usage

Typically you would call

my_object.stub!(:updated_at).and_return(time_object)
February 17, 2009
7 thanks

Usage examples

Basic usage:

User.should_receive(:find).with(:all, anything).and_return("hello world")

Now:

User.find(:all, :conditions => "foo")  #=> "hello world"

But you can also use blocks for more complex matching logic. For example:

User.should_receive(:find) { |*args|
  if args.size == 2
    "received two arguments"
  else
    "something else"
  end
}.at_least(:once)

Now:

User.find(:all, :conditions => "bar")  #=> "received two arguments"
User.find(5)                           #=> "something else"

Of course normally you’d return mocks instead of strings.

February 4, 2009
0 thanks
February 2, 2009
1 thank

usage

code

@items = Item.find(:all)
@items.should have_at_least(1).item
September 27, 2008
3 thanks

Example using simple_matcher

This is extracted from: http://blog.davidchelimsky.net/2008/6/7/thoughts-on-the-dance-off

Here’s an example:

def be_sorted
  simple_matcher("a sorted list") {|actual| actual.sort == actual}
end
[1,2,3].should be_sorted

The block is handed the actual value. If the block returns true, the expectation passes. If it returns false, it fails with the following message:

expected “a sorted list” but got [1, 3, 2]

If you say [1,2,3].should_not be_sorted you’d get this message instead=:

expected not to get “a sorted list”, but got [1, 2, 3]

As of now, you don’t get any control over the failure message other than the string you pass to the simple_matcher method

August 15, 2008
9 thanks

anything matcher

The anything matcher will match any ruby object:

1.should == anything
nil.should == anything
'string'.should == anything

var.should_receive(:method).with(param1, anything)
August 14, 2008
13 thanks

Testing an options hash receives certain parameters

This method is very useful for testing methods that use the ruby idiom of accepting a hash with configurable options.

class Example
  def self.find(options = {})
    ...
  end
end

We can use hash_including to ensure that certain options are passed in when mocking it.

Example.should_receive(:find).with(hash_including(:conditions => 'some conditions'))

Example.find(:conditions => 'some_conditions', :order => 1)
# => Passes expectation

Example.find(:order => 1)
# => Fails expectation

This can also be used to great effect with the anything matcher. For example:

hash_including(:key => anything)

hash_including(anything => 'value')