Notes posted to RSpec
RSS feeddescription 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
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.
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
Help Link :)
www.relishapp.com/rspec/rspec-mocks/docs/message-expectations/receive-counts!
This method is deprecated in rspec 2.0
-
be_close(1, 0.1) is deprecated.
-
please use be_within(0.1).of(1) instead.
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.
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
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.
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
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.
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
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')