I still reading The RSpec Book, which is amazing! I´m doing a little review of what i´ve been reading about.

1.1 Creating Expectations

There are two methods available for checking expectations: should() and should_not().

1.2 Built-in Matchers

There are several matchers that can be used with should and should_not, which are divided into well-separated categories.

1.2.1 Equality: Equivalence and Identity

 cow.should == twin_cow
 cow.should === twin_cow
 cow.should eql(cow)
 cow.should equal(cow)

The == method is used to express equivalence and equal is used when you want the receiver and the argument to be the same object.

Note: Instead of using !=, you should use the should_not method!

1.2.2 Floating Point Calculations

 result.should be_close(3.14, 0.005)

When dealing with floating points, sometimes you expect 3.14 but 3.1415 is returned, which would be a pain to express on expectations if it wasn´t for the built in matcher be_close(), which takes two arguments: the floating point number you are expecting and the precision you require.

1.2.3 Regular Expressions

 result.should match(/this regexp/)

result.should =~ /this regexp/

This can be very useful when dealing with multiple-line expectations, instead of using the open file technique to compare contents.

1.2.4 Changes

 lambda {

User.create!(:role => "admin" )

}.should change{ User.admins.count }
 ## OR

 lambda {

User.create!(:role => "admin" )

}.should change{ User.admins.count }.to(1)

 ## OR

 lambda {

User.create!(:role => "admin" )

}.should change{ User.admins.count }.from(0).to(1)

This is really useful when working with database changes or changes to objects. Another way of writing this is by using the before_state -> change -> after_state technique, as follows:

 total.price.should == 0

buyer.inserts Product.new(:price => 250)

total.price.should == 250

The matcher is change(), which takes a block with the object attribute/method and accepts the from(), to() or by() modifiers.

1.2.5 Errors

 field = SoccerField.new(:players => 20)

lambda {

field.remove(:players, 25)

}.should raise_error(NotEnoughPlayers,“attempted to remove more players than there is on field”)

Useful when needed to check for Exceptions. The matcher is raise_error and takes an ExceptionObject and/or a String/Regexp.

1.2.6 Throw

 speech = Speech.new(:seats => 100)

100.times { speech.register Person.new }
 lambda {

speech.register Person.new

}.should throw_symbol(:speech_full, 100)

When dealing with “errors that are not really exceptions”, you use catch and throw. Rspec can check if a throw has been called by using the throw_symbol matcher. It accepts 0,1 or 2 arguments. The first argument needs to be a Symbol and the second can be any Object that is thrown along.

1.3 Predicate Matchers

A Ruby predicate method is a method that ends with a “?” and returns a boolean value, like string.empty? or regexp.match? methods. Rspec allows us to write expectations to these methods in a beautiful, understandable way, instead of writing:

 a_string.empty?.should == true

We can write:

 a_string.should be_empty

1.3.1 The be_something method

When using a be_something matcher, RSpec removes the “be_”, appends a “?” and calls the resulting method in the receiver.

A very common construct of this method is be_true, which checks if the receiver is true (any object except false or nil) or false (false or nil).

1.4 Checking ownership

Sometimes you will want to check not the object itself, but something the object owns. In this case, RSpec allows you to write some beautiful sentences to check for owned objects.

1.4.1 The have_something method

 request_parameters.has_key?(:id).should == true
 # is the same as

request_parameters.should have_key(:id)

RSpec uses method_missing to convert anything that begins with have_something to has_something? and performs the checking.

1.4.2 The have() method

 field.players.select {|p| p.team == home_team }.length.should == 9

# is the same as

home_team.should have(9).players_on(field)

As have() does not respond to players_on(), it delegates to the receiver (home_team). It encourages the home_team object to have useful methods like players_on.

You can get a NoMethodError if the players_on method doesn´t exist, you can get another NoMethodError if the result of the players_on method doesn´t respond to size() or length() and if the size of the collection doesn´t match the expected size, you will get a failed expectation.

1.5 Checking Collections Themselves

Sometimes we create expectations about a collection itself and not about an owned collection. RSpec lets us use the have() method to express this as well, as in:

 a_collection.should have(10).items

items is just providing some meaning to the expectation.

1.5.1 Strings

Strings are not collections by definition but they respond to a lot of methods that collections do, like length() and size(). This allow us to use have() to expect a string of a specific length.

 “lucas”.should have(5).characters

characters is just providing meaning to the expectation as well.

1.5.2 Have() modifiers for precision

The have() method has some relatives that allow us to check for upper and lower conditions.

 work.should have_exactly(8).hours

basket.should have_at_least(5).items

auditorium.should have_at_most(100).people

1.6 Operator Expressions

There may be sometimes when you want to expect a value to be not an exact amount but something like greater than or less than. RSpec allows you to do this by using the regular operators from Ruby!

 number.should == 3

number.should be >= 2

number.should be <= 4

number should be > 0

1.7 Generated Descriptions

Sometimes the code within a expectation looks the same as the string used to describe it. In this cases, you can omit the string by using the specify() method, which is an alias of the it() method but reads better when there is no documentation string.

 describe “A new user” do
 specify { User.new().should be_confused }

 #is the same as:
 describe “A new user” do
 it “should be confused” do
 User.new.should be_confused

This should be used carefully and only in cases where the docstring and the code look exactly the same.

1.8 Subjects

Sometimes within an example group we want to use a subject on which we will develop the expectations. To create a subject you have to use the subject() method, which takes a code block creating the subject. Once you specify a subject, all the should() and should_not() methods can be called without a receiver as it will point to the subject in question.

 describe Rabbit
 subject { Rabbit.new(:age => 1) }
 specify { subject.should be_aged(1) }
 # or even better
 describe Rabbit
 subject { Rabbit.new(:age => 1) }
 it { should be_aged(1) }

You can also use implicit subjects when describing a class. RSpec will automatically create an instance of the described object, allowing us to write expectations like this:

 describe Rabbit
 it { should eat_carrots }

This should be used with caution though! You should not coerce your objects to fit into this model instead of writing it as you should really have written!

1.9 Matchers Table and Thoughts

Matcher Used to expect
==, ===, eql, equal (other) Equality between objects
be_close(other, precision) Floating Points
match, =~ (other) Regular Expressions
CodeBlock.should Change(&attribute)[.by(num)][.from(num).to(num)] Changes in attributes
CodeBlock.should raise_error([ErrorClass,[Error String/Regexp]]) Exceptions
CodeBlock.should throw_symbol([Symbol,[Object]]) Throws
be_xxx (like be_empty) Predicate methods (ending on ?)
have_xxx (like have_key) has_xxx? Methods
have(number)[.receiver_method] Number informations
be (==,>=,>,<=,<) Operators
  • The specify() method can be used when a docstring looks like the code within the expectation.
  • The subject() method takes a code block and can be used to specify a subject. Once specified, the subject becomes the receiver for every should and should_not.
  • Subjects can be automatically created when describing a class. RSpec calls the new() method to the object and use it as the subject to the following expectations.

RSpec Example Building

January 26, 2010

Some stuff I´ve learned while reading the RSpec book, which is very good as the latest beta version.

  • describe and context are synonyms. Describe is better for actions and Context is better for hm.. well.. contexts! They work by supplying a string that describe the behavior you are trying to test (or a reference to a class or a Module) and a code block where the expectations should be placed.
    # some google code...
    describe User do
    describe "should not be chinese" do
  • RSpec supports “nesting” to give context to your tests!
  • The it() method takes a string, an optional hash and an optional code block.
  • it() called with no block is marked as pending. Experienced developers recommend writing down everything you want the application to do, even when developing some other code if you remember something you missed, and leave it as pending until you get to it. Very easy to do, isn´t it?
  • Other ways to set a description to pending is by calling the pending() method inside of it. It even supports a code block that gets executed even with the description pending. If the code block succeeds, RSpec alerts you that it has been implemented (actually, it says it is fixed).
  • RSpec provides a before() method that takes supplied code block to be executed before all of the examples inside the context or before each of them. The only argument it takes is :all or :each. It´s highly recommended that you use :each as it starts a new context when checking an example.
  • The counterpart of the before method is the.. Hmm.. you guessed! The after method. It´s basically the same thing with the same arguments, except it runs after the examples have been executed, even if errors or exceptions are raised.
  • You can create helper methods that are methods defined inside the context that can be used by all the examples inside that context. It´s useful to keep things clean and DRY-compliant (Don´t Repeat Yourself). However, the goal is to make things clean but without loosing the readability. Keep things consistent!
  • To share helper methods across contexts, you can create a module with the methods and then include the module within the context.
  • You can share examples too! just start the example with shared_examples_for “name” do […] end and then call it within the context by using the it_should_behave_like “name” method.
  • Another way to share examples is by creating example modules, with the share_as :Name do […] end method. When needed, just include the created module inside the context by calling include Name.