Rspec-Given
Testing is a Given
I have played with a lot of testing frameworks. We Rubyists are forever obsessing about our testing. Be it testing like Usain Bolt or the TSA, either way, we be testing. And that is a very good thing.
I subscribe heavily to making my code as readable and understandable as possible. I, for one, read more code than I write. The first place I always look is the tests. The integration and functional tests give me the 10,000 foot view of the context, the units tests tell me about the mechanics and edge cases. And being a developer of this ilk, I strive to make my tests as expressive as possible for the next developer following my code story. Gone are the days of information hoarding and being the specialist in that piece of code. Leave that to those other devs. Don’t be that guy.
Lately I have been toying with the rspec-given extension for Rspec from none other that Jim Weirich ( and I’m pretty sure I don’t have to tell you who he is). We all know the expressive syntax sugar we get from Rspec, and we may like the clarity of a Cucumber features. rspec-given brings those Given, When, Then declarations into your tests, along with a couple of extras that certainly hit my happy gong.
The Basics
Getting rspec-given into your specs is trivial, just add the gem to your Gemfile and require it in the spec_helper.rb
a la:
[gist id=3889881]
And then you are all set for the new hotness in declarative syntax that really implies the intent of the code under test.
The syntax follows the Given/When/Then philosophy of Given
being setup, When
being exercising or ‘poking’ and Then
for expectations and assertions.
An example of the syntax would be as follows:
[gist id=3889895]
You will note the absence of a When
block here as we are only testing state, the number of beers on the wall, initially. Using the familiar Rspec describe
and context
blocks define what we are testing. The Given
block takes an optional symbol, which if you are a regular user of Rspec will be familiar, to let(:something){ lazily_evaluate_to_something_else}
. You will note the snazzy new Rspec expectation and assertion syntax being used in the Then
block.
On a side note, I have never really liked using the word “should” in my tests. I realise that, yes, we are writing it before the implementation so we are looking to the future of the code’s capabilities. I don’t know, it feels a bit fuzzy and almost OK if what I’m testing is only correct some of the time? When writing a specification of how my code should behave, I like Test::Unit
‘s assert
this is that, and now mintiest uses must_*
. These are clear expectations for the code, far superior to should
.
Poking and What Not
Now we have tested some state in our code it is time to exercise a piece of code and test the output. Here we use the When
syntax.
[gist id=3889900]
Putting aside the fact that I’m describing not only being a famous drinking rhyme, you can read that test and know exactly what we are trying to achieve. Setup, Poke, Assert. Of course This is clearly a simple example, but we can extend this with multiple setups across multiple blocks of context.
[gist id=3889903]
Rspec-given also brings with it an And
syntax (Note: I had to use the latest beta to get this syntax, 2.1.0.beta.4). At first glance And
looked an alias of Then
until you read the documentation. Yes it is a Then
assertion, only it will re-use the setup of the mandatory Then
that precedes it. Thus, when you have a lengthy setup, tested your first assertion, and wish to further test state without repeating the setup, And
is your friend. What immediately springs to mind is when I parse CSV files, I can run the setup just once and test every entry without ever re-running the initial setup. Hardly a lengthy setup involved, but certainly nice to remove scaffolding a new test when we have the require state to hand.
Another nice feature of rspec-given is the Invariant
syntax. This block is another Then
assertion that is injected into every context applicable to it. Invariant
is meant to test conditions which remain constant throughout the code under test. If we return to our beer drinking song, we could write:
[gist id=3889906]
But, you know what? I rather wouldn’t. Don’t get me wrong, the concept of Invariant
is great and when used in a better context than beer songs it can be really helpful. No, for beers songs I, personally would rather sacrifice syntax for readability and create a new context of the test (while showing how easy it is to modify your setup within different contexts).
[gist id=3889910]
If we look to the Rspec-Given documentation we see a much more applicable example, where the assertion is far clearer.
[gist id=3889912]
From that point on you can get testing in earnest. The personal highlight has to be the simplification of setups, especially across context. With pure Rspec I would write:
[gist id=3889914]
By comparing the two, we can see how much is omitted. The it
blocks become unnecessary noise and to dry up the test code we would have to refactor the setup into a method or helper, setting up parameters and so on. Using the Given
syntax not only shortens the code but gives it a far more implicit nature.
Going Just a Bit Deeper
Another highlight of rspec-given for me is how it doesn’t monkey patch anything in Rspec. It is written as an extension. I have gone as far as creating custom helpers and assertions for Rspec, but I have never really extended it in any way.
If we look at the configuration for rspec-given we can see it pulling on the power of the existing Domain Specific Language (DSL).
[gist id=3889919]
It uses extend
and include
methods on the Rspec::Core::Configuration
instance. These are pretty similar in function where extend
adds to the example groups (describe
and context
) and include
to the examples (it
blocks). This in itself is a really neat feature of Rspec and something I will be playing with in the future, all thanks to rspec-given.
Wrapping up
In my opinion rspec-given is a very welcome addition to the Rspec suite. Of all the developers I know using it (and to be fair that isn’t many at the moment) all have very good things to say about it. It’s a pretty compact little extension that provides a heck of a lot for spec suites. I have found, in general, the specs are much tidier and really relay the intent of the code. These are very useful aspects of a DSL which we should all aspire to creating.
The added benefit has been showing what a full-featured test framework Rspec is. Steven Baker and David Chelimsky have really done a great job building the framework to be so flexible. How easily Jim has hooked into Rspec’s DSL and brought out some really impressive syntax keeps me awake at night cursing all their names for being so damn good.