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 bdd, rspec | 2 comments
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
Create your first spec by creating snowball_spec.rb and adding the following to it.
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.
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.
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 bdd, rspec, ruby | no comments