In the Ruby world, we have a few different testing libraries. One of the ones that has gained a lot of popularity and use is rspec. RSpec takes a slightly different approach to the idea of testing applications, by testing behavior rather than only specific methods. I thought I’d take some time to show everyone why rspec is so useful in testing your applications.
The Basics
RSpec gives you a way to encapsulate what you’re testing via the describe block, and it’s friend context. In a general unit testing sense, we use describe to describe the behavior of a class:
describe Hash do end |
Tests are written using the it block. Here’s an example of how you might write a spec for the Hash class:
describe Hash do it "should return a blank instance" do Hash.new.should == {} end end |
Those are the basics of writing some rspec examples. You can run the above by installing rspec via gem install rspec and putting that code in a file called hash_spec.rb and typing:
$ rspec hash_spec.rb |
You should see something like the following:
. Finished in 0.11021 seconds 1 example, 0 failures |
You can set up test state using the before and after directives. This will apply to anything in your describe block:
describe Hash do before do @hash = Hash.new({:hello => 'world'}) end it "should return a blank instance" do Hash.new.should == {} end it "hash the correct information in a key" do @hash[:hello].should == 'world' end end |
The above code will create the @hash variable before each test is run. There are two arguments for before– all and each. Using the all argument, the setup will be done once before all of the tests in the block, and :each will be done before each individual test. The after directive has the same options and runs the same way, only after tests are completed. This is most useful when tearing down the previous state of the tests.
RSpec Idioms
We usually use the describe keyword to describe methods. Using a “.” will signify that you’re testing a class method, and using “#” will signify that it’s an instance method. Here’s how it might look for a made up class:
describe MyClass do describe ".class_method_1" do end describe "#instance_method_1" do end end |
The context method does the same thing by letting you contextualize a block of your tests. This is extremely powerful for test states when you add more complicated setup and teardown code to really get in to your objects. I’ll show you a bit more of a real life scenario by building a delicious burger class.

Let’s say that in our Burger class we’re trying to test the #apply_ketchup method. Someone may not want ketchup on their burger. Instead of judging them, we’ll write a test for the class to not apply ketchup if someone doesn’t want it:
describe Burger do describe "#apply_ketchup" do context "with ketchup" do before do @burger = Burger.new(:ketchup => true) @burger.apply_ketchup end it "sets the ketchup flag to true" do @burger.has_ketchup_on_it?.should be_true end end context "without ketchup" do before do @burger = Burger.new(:ketchup => false) @burger.apply_ketchup end it "sets the ketchup flag to false" do @burger.has_ketchup_on_it?.should be_false end end end end |
Cleaning Up a Bit
The above pattern works but can become a bit tiresome to repeat all the time. RSpec gives us some helper methods to generalize it. We could rewrite the above using the
describe Burger do describe "#apply_ketchup" do context "with ketchup" do let(:burger) { Burger.new(:ketchup => true) } before { burger.apply_ketchup } it "sets the ketchup flag to true" do burger.has_ketchup_on_it?.should be_true end end context "without ketchup" do let(:burger) { Burger.new(:ketchup => false) } before { burger.apply_ketchup } it "sets the ketchup flag to false" do burger.has_ketchup_on_it?.should be_false end end end end |
This all works but we can clean it up even further using the subject method. The subject method tells rspec what we’re doing the tests on. We’re going to combine that with the specify method in the next example. The specify method is just like the it method except the specify method takes the code block as the description of the test:
describe Burger do describe "#apply_ketchup" do subject { burger } before { burger.apply_ketchup } context "with ketchup" do let(:burger) { Burger.new(:ketchup => true) } specify { subject.has_ketchup_on_it?.should be_true } end context "without ketchup" do let(:burger) { Burger.new(:ketchup => true) } specify { subject.has_ketchup_on_it?.should be_false } end end end |
One neat thing about rspec is that the built in matchers will let you declaratively specify methods in your tests if they conform to a certain naming convention. RSpec will look for methods that are named with has and end in a question mark to let you do write declarative test code. Here’s what our final Burger class will look like using that idiom. Put the following in a file called burger_spec.rb and run it:
class Burger attr_reader :options def initialize(options={}) @options = options end def apply_ketchup @ketchup = @options[:ketchup] end def has_ketchup_on_it? @ketchup end end describe Burger do describe "#apply_ketchup" do subject { burger } before { burger.apply_ketchup } context "with ketchup" do let(:burger) { Burger.new(:ketchup => true) } it { should have_ketchup_on_it } end context "without ketchup" do let(:burger) { Burger.new(:ketchup => false) } it { should_not have_ketchup_on_it } end end end |
A Burger Needs More Than Just Ketchup
In this brief tutorial we jumped in to rspec and created a burger class. Then we refactored our tests a bit to make them more idiomatic. What we wound up with was easier to read and quick to run. In the next tutorials in this series, we’ll delve deeper in to rspec including more idioms, further testing, and writing our own matchers. For now, I know what I’m having for lunch.



Comments