RSpec 1.0 tutorial - before and after

Posted by Ken Brooks Thu, 24 May 2007 22:49:00 GMT

Tonight we’ll be starting down the road of those refactorings I promised and along the way we’ll introduce before and after.

First lets change that snowball itself to be a little more rubyish. In this case that means we will use symbols for the color.

class Snowball
    def initialize
        @color = :WHITE
    end

    def color?(color)
        @color == color
    end
end

Now we can modify the spec to do the same, passing in symbols for the color param.

require 'snowball'

describe Snowball do

    before(:each) do
        @snowball = Snowball.new
    end

    it 'Should NOT be yellow' do
        @snowball.should_not be_color(:YELLOW)
    end

    it 'Should be white' do
        @snowball.should be_color(:WHITE)
    end

    after(:each) do
        @snowball = nil
    end
end

You may have noticed that I did a little more than change the params to use symbols. There are now calls to before and after. If you are from an xUnit background you can think of these as setup and teardown. The before method runs prior to each example. before(:each) is the same as before because :each is the default param. The after method runs post each example. The same default of :each applies to after so after(:each) is identical.

Updated 2007-05-25

Aslak (Core developer of RSpec) was kind enough to point out something that I should have clarified:

Each example (instance) runs in a new instance anyway, and the instance variables are garbage collected. The after(:each) here has no effect, and since it has no effect, doing it is confusing.

So in my example above the after method is not doing anything of value. That @snowball will be garbage collected without having to set it to nil. I just needed something to plop in there to show that I could be doing something.

End Update

There are also before(:all) and after(:all) that can be used. These are mostly discouraged as it may cause side-effects by having dependencies between examples. I came across a scenario at work (in Java-land) where this actually applies. I was using JUnit to write a func test that can be used as a test client of a deployed J2EE service. Before any calls can be made to the service we must initialize (authenticate). Once that initialization is done then we can call our service as many times from anywhere in our code using a static service wrapper. So in my test since there is only one setup method I was forced to write code similar to this:

    private static boolean initialized;

    public void setUp() {

        if(!initialized) {
            SubSystem.init();
            initialized = true;
        }

        doSomeOtherSetupTask();
    }

That saves the overhead of authenticating before each test. Unfortunately its ugly to me.

In rspec (altho I’m not sure I would use it to drive a client test like I did with JUnit) I suppose that code would look like this:

    before(:all) do
        sub_system.init
    end

    before(:each) do
        do_some_other_setup_task
    end

Now that is tasty goodness.

Tags ,  | 2 comments

RSpec 1.0 and tutorial - Basics

Posted by Ken Brooks Mon, 21 May 2007 00:30:00 GMT

RSpec has reached 1.0.0 (congrats to that team).

What is RSpec you ask? Straight from their site:

RSpec provides a Domain Specific Language with which you can express executable examples of the expected behaviour of a system.

A little better (and foreshadowing) explanation from their site:

…use RSpec to #describe Behaviour of a system using Examples of how #it should work.

Over the next few posts I’ll be going thru some of the features with small tutorials.

First up, the basics of installing RSpec and writing your first working spec. We’ll be going thru the specs of a snowball, now that summer is here and I spent the day sweating my snowballs off.

Get rspec

sudo gem install rspec

Create your first spec by creating snowball_spec.rb and adding the following to it.

describe Snowball do
end

That is telling us that we are going to describe the behavior and requirements for a successful implementation of a snowball.

Now is as good a time as any to fire up rspec and see what the magic is all about.

spec snowball_spec.rb

That just produced a ./snowball_spec.rb:1: uninitialized constant Snowball (NameError) message. Not exactly shaping up to be the greatest tool in the world is it? Take a closer look. Its actually telling you where to go next. Yep, create a Snowball class.

class Snowball
end

Don’t bother running spec again as you’ll be presented with the same error again. You need to tell the spec we depend on that snowball class. Modify your snowball_spec.rb to require the snowball.

require 'snowball'

describe Snowball do
end

Now if you run spec again you should see a little more positive results.

$ spec snowball_spec.rb 


Finished in 7.0e-06 seconds

0 examples, 0 failures

That basically tells us that we are ready to really start describing the behavior of our snowball.

Lets add an example to our spec. We are going to make the statement that snowballs should not be yellow (unless you really don’t like the kid down the street).

require 'snowball'

describe Snowball do
    it 'Should NOT be yellow' do
        snowball = Snowball.new
        snowball.should_not be_color("yellow")
    end
end

That is pretty easy to read, and if you now run spec with the --format specdoc it finally hits you why RSpec is different and has its niche as compared to xUnit tests. We really are documenting the behaviour of a system and writing code to meet that required behavior. Now you can have your Keanu Reeves (Neo) ‘Whoah’ moment.

Ok, enough of that moment, lets finish up the snowball code to make this spec run again. And I’ll get back to creating that snowball instance in the middle of the test. If I don’t then please ping me and remind me.

class Snowball
    def color?(color)
        color == "white"
    end
end
$ spec snowball_spec.rb --format specdoc

Snowball
- Should NOT be yellow

Finished in 0.006263 seconds

1 example, 0 failures

Without changing the Snowball class, we should now try and see if the positive test works also.

require 'snowball'

describe Snowball do
    it 'Should NOT be yellow' do
        snowball = Snowball.new
        snowball.should_not be_color("yellow")
    end

    it 'Should be white' do
        snowball = Snowball.new
        snowball.should be_color("white")
    end
end

Obviously (for the more experienced users) there are a few things that will need to be refactored out but in the interest of simplicity I left them that way to start.

That covers the basics of using RSpec. We described the behavior of an object and then created some examples of its behavior.

Next section we’ll dig a little deeper and start doing those refactorings.

Tags , ,  | no comments