I’m going to try and turn my recent presentation on RSpec into a series of blog posts – This being the first. Before I get on with it I would like to thank the Houston Ruby and Rails Users Group for having me. They asked some pretty insightful questions and were generally a great audience. I’d also like to thank ThoughtWorks for paying for my flight, rental car, and lost billable hours so I could do this presentation.
RSpec without Rails
So in order to show you how RSpec works, I’m going to start with specifying a class that deals with string case like so:
After requiring a spec_helper and the file I intend to spec, I set up a ‘describe’ block. Inside the block there is a single spec (or example) which is also a block. (If you’ve looked at RSpec before you might wonder what happened to context and specify -- they’ve been replaced by ‘describe’ and ‘it’ In the example I declare a the name of the spec to be “should upcase a string” which does not execute and could be any name you like. By convention they all start with “should.” Then I create a new StringCaser and give it a string. On the next line I call caser.upcase which should equal “A STRING”. Instead of having to remember which is expected and which is actual, the RSpec way is to simply do what you want and then call ‘should’ on the output. Much more readable. If you’re wondering, should and should_not can be called on any Ruby Object.
So If I run the above spec without creating the file, I’ll get an error on the require. So let’s create a file called string_caser_spec.rb
And if run the spec like so:
I get:
Because I forgot to upcase the return value. Oh well, easily fixed:
Now run rspec so that it formats the output like so:
And I get this output:
Lets add another right below the previous spec:
Now I’m saying that a StringCaser instance should know if its content is all lowercase. But this time I’m using RSpec’s built in support of interrogatives. When RSpec sees ‘be_’ in a matcher (which is the thing that comes after should), it looks for a method with a name that follows ‘be_’ and has a ‘?’ at the end. In this case ‘be_lower_case’ makes RSpec look for a method called ‘lower_case?’ and calls it. There a lot of nice syntactic sugar like this in RSpec – way more than I could ever hope to cover. Now ‘lower_case?’ doesn’t exist yet so I get an error, and then I write this code:
And I’m back to passing. Btw, you can call rspec with -c to print your output in color, if you like.
Now for something a little more interesting. I want StringCaser to throw a RuntimeError if it receives an object that does not respond to ‘upcase.’ Here’s the spec:
Cool, huh? Sure I could do this in Test::Unit, but it would have to be one test with a generic name. In RSpec I can name on the fly (yes I could use some Ruby magic to define methods at runtime in Test::Unit, but think of the readability! (or lack there of)).
So now I add some code to StringCaser:
and when I run my specs with spec string_caser_spec -fs, I get:
Which is nice. There’s also a way to get this output as an html file.
Now I find that as I’m writing unit tests that I often end up with a bunch of different setups, or one big complicated setup that only 1/5 of which is important to any one test. Either case kinda sucks, but RSpec gives me a way to segregate my setups like so:
Now I can I have a place where I specify for each context. This tends to drive out more specs and produces a more complete suite. Warning, this does not mean I’m endorsing huge setup methods. A spec should be readable in and of itself. And yet, I think a short setup that does exactly what the ‘describe’ says is a useful thing.
Next time I’ll talk about testing/specing/examplifying Rails Models.