[rspec-users] [Q] how to restructure tests for an abstract class?

Chuck Remes cremes.devlist at mac.com
Tue Feb 26 14:22:35 EST 2008

On Feb 25, 2008, at 8:30 AM, David Chelimsky wrote:

> On Mon, Feb 25, 2008 at 6:23 AM, Chuck Remes  
> <cremes.devlist at mac.com> wrote:
>> Thanks for asking this question. This is exactly what I was going  
>> to write,
>> but you beat me to it!
>> (Sorry for the top-post; just following the last responder.)
>> cr
>> On Feb 25, 2008, at 3:30 AM, Matthijs Langenberg wrote:
>> Question is, would you duplicate the specs for all the classes that  
>> include
>> a certain module (through shared behaviour for example), or would  
>> you use
>> one set of specs for just the module, and specify that a class should
>> include that module?
> Matthijs - I'd throw the same question back to you. Have you tried
> both approaches? How have they worked out for you?
> I won't wait for your answer :). But I am curious about other people's
> experiences with this.
> I can tell you this from my own experience - I tend to use shared
> groups for this for a couple of reasons. One, I like to see the specs
> for each object.

I thought I'd share my experiences with the group. Please recall I'm  
new at BDD/TDD so I may get some of the terminology wrong or whatever.

So I have a class that, by itself, doesn't do anything. Concrete  
subclasses are necessary to flesh out a few characteristics before the  
parent class code can perform its magic. This is an abstract class  
which I'm told isn't the Ruby Way, so I'm looking at turning it into a  
module (a topic for another message).

Originally, I was curious how to refactor my classes (and tests). I  
decided to write a second concrete subclass to see what kind of  
problems I might run into. I figured *any* problem I encountered was  
just more information for me to figure out the correct direction. The  
second subclass started out normally enough. I spec'd some behavior  
unique to that subclass. So far, so good. Then it occurred to me that  
I had no idea if the parent class was really being exercised by my new  
tests; turns out it wasn't being exercised.

To resolve this I started adding some tests to make sure I covered the  
original behavior of the parent class. Now I had code duplication. The  
parent class tests and my *first* subclass were duping some of the  
same behavior. Looking into the examples directory I saw the concept  
of #shared_examples_for.

I refactored my tests using shared examples. Most of these went into  
the parent class (perhaps soon to be a module). All the tests in my  
subclasses then focused exclusively on the behavior unique to that  
specific class while the describe blocks called #it_should_behave_like  
for shared behaviors. This DRY'ed the code up considerably.

An added benefit was some test breakage I ran across while refactoring  
the tests. My second subclass had some rather tight coupling to the  
tests, so when I made it shared the subclass test broke. It forced me  
to rethink some of the test and class design to loosen the coupling.  
Ultimately it led to a better class api.

So, that's my rambling summary. Kudos to you if you read this far.

My next goal is to DRY up some of my 'before (:each)' blocks. I  
continually do the same setup operations across #describe blocks (@buf  
= Buf.new; @msg = blah, etc). It looks like I may want to define  
subclasses of a parent Spec::ExampleGroup so the subclasses can  
inherit some of the #before setup. I'd love to hear experiences from  
others on this technique.

Lastly, I thought I'd say a word on the resulting class code. This BDD  
project was a learning experience. I rewrote a set of classes that I  
had originally written the old-fashioned way; puzzle through the logic  
in my head, write the code, and then debug the crap out of it until it  
worked. The original classes are rather short (LOC) with only a few  
methods (3 or 4). The classes I wrote via BDD are longer, maybe by 40%  
in terms of LOC. Plus, I now have around 10 methods none of which  
exceeds 5 lines of "real" code. It's more readable, more logical, and  
*far* easier to subclass. I'm now a believer.



More information about the rspec-users mailing list