[rspec-users] [Q] how to restructure tests for an abstract class?
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.)
>> On Feb 25, 2008, at 3:30 AM, Matthijs Langenberg wrote:
>> Question is, would you duplicate the specs for all the classes that
>> 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
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