What's the best way to test inherited behavior?

I currently have a base class and 2 subclasses. I’m struggling with
finding the best way to test them. This is the current situation.

The base class is called AbstractServer. It’s not really abstract
because it can be instantiated and used, but some important methods are
defined as no-ops, and are meant to be overrided by child classes. The
spec for AbstractServer looks like:

describe AbstractServer do
it_should_behave_like “AbstractServer-like behavior”

 ... AbstractServer tests ...

end

For the child classes, their specs look like this:

describe ApplicationSpawner do
it_should_behave_like “AbstractServer-like behavior”

 ... ApplicationSpawner-specific tests ...

end

describe FrameworkSpawner do
it_should_behave_like “AbstractServer-like behavior”

 ... FrameworkSpawner-specific tests ...

end

However, this looks very ugly. ‘it_should_behave_like
“AbstractServer-like behavior”’ doesn’t read like a normal sentence, and
saying that AbstractServer has AbstractServer-like behavior is
redundant.

What’s the best way to solve this? What are good practices for testing
inherited behavior? Should I be testing my child classes for parent
class behavior at all? Right now I’m doing it anyway in order to detect
bugs that I might have missed otherwise.

On Thu, Oct 9, 2008 at 12:20 PM, Hongli L. [email protected] wrote:

What’s the best way to solve this? What are good practices for testing
inherited behavior? Should I be testing my child classes for parent class
behavior at all? Right now I’m doing it anyway in order to detect bugs that
I might have missed otherwise.

Not sure why what you’re doing works at all, but try this (which is
how it should work):

describe “AbstractServer”, :shared => true do

end

describe ApplicationSpawner do
it_should_behave_like “AbstractServer”

end

etc

On 9 Oct 2008, at 18:20, Hongli L. wrote:

I currently have a base class and 2 subclasses. I’m struggling with
finding the best way to test them. This is the current situation.
What’s the best way to solve this? What are good practices for
testing inherited behavior? Should I be testing my child classes for
parent class behavior at all? Right now I’m doing it anyway in order
to detect bugs that I might have missed otherwise.

I’ve been meaning to blog about this, as I recently had an epiphany
about how brilliantly rspec handles this, IMO.

I actually do it pretty much the way you’re doing it, but I don’t
think it’s ugly at all.

To me, the abstract superclass is just an implementation detail of the
concrete subclass - I don’t need to or want to care that
implementation detail when I’m writing specifications about how the
concrete class should behave. Therefore while the behaviour which is
common between it and other classes that have the same superclass does
tend to end up in shared example groups, that’s just because I’m
keeping my code nicely factored - the same specs would still be valid
if I copied and pasted the superclass code out into the two subclasses
and collapsed the hierarchy, or moved them into modules, or whatever.

It’s a subtle shift, but if you try to let go of the implementation,
and think about the behaviour when you name your example groups,
you’ll find they start to read more naturally.

In your case, the two concrete servers can just say

it_should_behave_like “a server”

and pull in the specifications for the behaviour that is common to both.

At songkick, for example, we’re modelling bands playing gigs. We have
an abstract Event superclass, subclassed by Concert and Festival. The
spec for Event reads like this:

describe Event do
it “should be abstract”
lambda { Event.new }.should raise_error(TypeError)
end
end

Then the two subclasses mix in a module containing the shared example
groups, and at the top, they say

describe Concert do
extend SK::Spec::Models::ExampleGroups #shared example groups for
models live in here
it_should_behave_like “an event”

special concert behaviour spec’d here…

I’ve tried to find a pattern I was comfortable with for this with
xUnit tests, and never managed it. For me, rspec’s solution fits the
problem like a glove.

HTH,
Matt

I’ve written [1] about using shared example groups to do this sort of
things. You’re already using them :slight_smile: but maybe you can still get
something out of that post.

What specifically don’t you like about this solution?

Pat

[1]

David C. wrote:

Not sure why what you’re doing works at all

Oh oops, I forgot to mention that I also have ‘shared_examples_for
“AbstractServer-like” behavior’.

Pat M. wrote:

I’ve written [1] about using shared example groups to do this sort of
things. You’re already using them :slight_smile: but maybe you can still get
something out of that post.

What specifically don’t you like about this solution?

What Matt proposed is very nice. :slight_smile: I was struggling with finding a good
way to name my specs.

However, by calling the shared example “a server” I can still end up
with weird names:

describe FrameworkSpawner
describe “when in conservative spawning mode”
before :each do

end

   it_should_behave_like "an AbstractServer"
 end

 describe "when in smart spawning mode"
   before :each do
   end

   it_should_behave_like "an AbstractServer"
 end

end

So I can end up with a weird sentence like:
“FrameworkSpawner when in smart spawning mode an AbstractServer raises
AlreadyStarted if the child process is still running”

Shared example groups are a blessing. Just the constructed sentences
bother me sometimes. :slight_smile: I’m also wondering whether it’s a good idea to
test my child classes like this, or whether I should test the parent
class separately, and not test the child classes for the parent class’s
behaviors.

Another approach would be to prefer composition over
inheritancehttp://www.google.com/search?q=prefer%20composition%20over%20inheritance,
and either inject the server in a constructor (with a suitable name for
the
class) or mix it in as a module.

Then you could describe the common server behaviour in one set of
examples,
and just have a couple of mock-based examples to verify that the
“subclasses” interact correctly with the Server.

Cheers,
Dan

2008/10/10 Matt W. [email protected]

On 9 Oct 2008, at 20:35, Hongli L. wrote:

Pat M. wrote:

I’ve written [1] about using shared example groups to do this sort of
things. You’re already using them :slight_smile: but maybe you can still get
something out of that post.
What specifically don’t you like about this solution?

What Matt proposed is very nice. :slight_smile: I was struggling with finding a
good way to name my specs.

ta! :slight_smile:

end
“FrameworkSpawner when in smart spawning mode an AbstractServer
raises AlreadyStarted if the child process is still running”

You’re right, these do read funny don’t they?

I still don’t think you’ve got far enough along the implementation-

behavioiur spectrum in your example names and that’s partly why it
still feels weird. I’d rid of that reference to the AbstractServer
altogether, for starters.

Don’t forget that the name of the shared example group does not go
into the specdoc - it’s just a label you use when pulling it in with
it_should_behave_like.

Maybe you could put another ExampleGroup block inside your shared
group to describe the context?

e.g.: “FrameworkSpawner when in smart spawning mode when the child
process is still running should raise an AlreadyStarted error”

Also bear in mind that you can split your shared behaviour and have
more than one shared example group, and ‘mix them in’ to define the
appropriate behaviour in different contexts.