[rspec-users] sharing specs in a subclass

Scott Taylor mailing_lists at railsnewbie.com
Thu Jan 24 10:19:13 EST 2008


On Jan 24, 2008, at 4:36 AM, Pat Maddox wrote:
>
> On Jan 22, 2008 10:26 PM, David Chelimsky <dchelimsky at gmail.com>  
> wrote:
>> On Jan 23, 2008 12:04 AM, Scott Taylor  
>> <mailing_lists at railsnewbie.com> wrote:
>>>
>>> On Jan 23, 2008, at 12:02 AM, David Chelimsky wrote:
>>>
>>>> On Jan 22, 2008 10:49 PM, Jonathan Linowes
>>>> <jonathan at parkerhill.com> wrote:
>>>>> Hi
>>>>>
>>>>> I've spec'd a class and they pass.
>>>>>
>>>>> Now I'd like to assure that any subclass of this class also passes
>>>>> the same specs.
>>>>> Any suggestions for a clever way to handle this?
>>>>> I'd prefer to keep the existing specs as is (eg instead of moving
>>>>> everything into shared behaviors, or doing something to all the
>>>>> 'describe' lines)
>>>>
>>>> How about:
>>>>
>>>> [Subclass1, Subclass2, BaseClass].each do |klass|
>>>>   describe klass do
>>>>   ...
>>>>   end
>>>> end
>>>
>>> That's sort of funny, being that I posted this solution on  
>>> Courtney's
>>> blog yesterday.
>>
>> I saw that. I've also done this before :)
>>
>>> Is this what you actually use in this sort of situations?  Are there
>>> other (better, or worse) alternatives?
>>
>> I usually stick w/ shared example groups, but I sometimes use an
>> iterator like this. Not sure why. I'll think about it.
>
> I think using an iterator here is weird, and muddles the intent of  
> the specs.
>
> Think of what it means to define a class and a subclass.
>
> class Foo
>   ... some behavior ...
> end
>
> class Bar < Foo
>   ... all of Foo's behavior ...
>   ... some specialized behavior ...
> end
>
> You don't define all the behavior in Bar - you get a bunch of it from
> Foo for free, and then specialize whatever you need.  Your specs
> should be similar in spirit.

In a normal setting, I'd agree with you.  In the case where I used  
it, there was STI involved, and the subclasses had almost no unique  
behaviour (except one or two methods which were over-ridden).

>
> shared_examples_for("the foo role") do
>   it "should have some behavior"
>   ...
> end
>
> The downside is you end up with
>
> describe Foo do
>   it_should_behave_like "the foo role"
> end
>
> to specify the behavior, which feels pretty weak to me.

The trouble with the shared specs is that you have to do the same  
setup multiple times, in various specs.

>
> Though I assume you feel somewhat similarly, so I'd be interested to
> know when you use an iterator for full example groups.
>
> Actually one example I remember is when I wrote a macro that added a
> bunch of methods to a class...I wrote the initial example group for
> just one attribute name.  Then when I extracted the macro, I just
> iterated through a bunch of names over the example group.  So it can
> be useful.  Although a whole class feels too coarse grained for that
> approach, to me.
>
> I do use iterators a bunch for stuff like
>
> [:name, :email, :phone, :address].each do |field|
>   it "should require a #{field}" do
>     u = build_user(field => nil)
>     u.should_not be_valid
>     u.should have(1).error_on(field)
>   end
> end
>
> Also, since I'm close to the subject, here's a little tip.  I've seen
> people write specs like:
>
> it "should require valid attributes" do
>   [:name, :email, :phone, :address].each do |field|
>     u = build_user(field => nil)
>     u.should_not be_valid
>     u.should have(1).error_on(field)
>   end
> end
>
> but that sucks, because if the example fails, you don't have any clue
> why.  Cause it'll just say that user is invalid, but doesn't tell you
> which field is blank.

Yeah, I agree.  It's also violating the one test per test case rule.   
If you had ordered the tests in the opposite manner, it wouldn't be a  
problem.

I usually still use valid?, even though it's less informative, since  
I let FixtureReplacement take care of making it a valid record in the  
first place:

it "should not be valid with a firstname" do
   new_user(:firstname => nil).should_not be_valid
end

it "should be valid with valid attributes" do
   new_user.should be_valid
end

Scott



More information about the rspec-users mailing list