[rspec-users] sharing specs in a subclass

Pat Maddox
2008-01-24T09:36:23Z

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.

shared_examples_for("the foo role") do
  it "should have some behavior"
  ...
end

describe Bar do
  it_should_behave_like "the foo role"

  it "should have some specialized behavior"
  ...
end

That is far more clear to me.  The downside is that you also end up with

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.

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.

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.

That was a bit of a tangent.  It''s late, I''m loopy :)

Pat