Notes posted to RSpec
RSS feedstub_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
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
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
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.
Examples
see have_at_least or have
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
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')

