Ovid has asked for the wisdom of the Perl Monks concerning the following question:

Summary: Test::Class setup and teardown methods should call their SUPER equivalents, if present.

In Test::Class, aside from marking methods as tests, you can also mark them as startup, setup, teardwown, and shutdown. For the sake of this question, I'll just focus on the setup and teardown methods. These run before and after every test method, respectively. You might see something like this:

sub pre_test : Test(setup) { # add objects to the database } sub get_objects : Tests { # get the objects and test 'em } sub post_test : Test(teardown) { # clean the database }

This is fine, but it doesn't work as well when you're inheriting tests (something to avoid, but it happens). Your derived classes really want to call the parent classes' setup methods but this doesn't happen automatically.

Here's how to replicate this:

The base class:

package TestBase; use base 'Test::Class'; INIT { Test::Class->runtests } 1;

A parent class with tests:

package Foo; use Test::More; use lib '.'; use base 'TestBase'; sub setup : Tests(setup => 1) { ok 1, 'Foo: should be called first'; } sub some_test : Tests(1) { ok 1, 'Foo: some test'; } sub not_overridden : Tests(1) { ok 1, "Let's hope this isn't dependent on Foo's setup method"; } 1;

And a subclass:

package Bar; use Test::More; use lib '.'; use base 'Foo'; sub setup : Tests(setup => 1) { ok 1, 'Bar: should be called second'; } sub some_test : Tests(1) { ok 1, 'Bar: some test'; } 1;

If you execute the subclass, Bar, you get the following:

1..8 ok 1 - Bar: should be called second ok 2 - Let's hope this isn't dependent on Foo's setup method ok 3 - Bar: should be called second ok 4 - Bar: some test ok 5 - Foo: should be called first ok 6 - Let's hope this isn't dependent on Foo's setup method ok 7 - Foo: should be called first ok 8 - Foo: some test ok All tests successful. Files=1, Tests=8, 0 wallclock secs ( 0.11 cusr + 0.02 csys = 0.12 C +PU)

What's happening is that every test class is loaded and the Test::Class->runtests in the base class is running every test class but we can see that when Bar is run, Foo's setup method is not called and this seems like a mistake since the parent setup methods are often setting up things that their methods require. The solution almost looks like:

sub setup : Tests(setup => 1) { my $test = shift; $test->SUPER::setup; ok 1, 'Bar: should be called second'; }

That doesn't work because I've declared a test count for each setup method and that throws my test count off (running two tests instead of one). Does this look like a bug in Test::Class or have I missed something here?

Update: you can get the superclass setup methods to be called if if you don't override them by providing a subclass method of the same name, but then they're called in alphabetical order. As I've painfully discovered, that breaks things if your subclass setup method relies on something your superclass setup method creates.

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: Test::Class and setup methods
by adrianh (Chancellor) on Mar 14, 2007 at 12:11 UTC
    Does this look like a bug in Test::Class or have I missed something here?
    I think you've missed the little-used "+1" syntax for extending the number of tests run by a method in a subclass. For example:
    { package BaseClass; use base qw( Test::Class ); use Test::More; sub setup : Tests( setup => 1 ) { pass "base setup"; } sub test: Test { pass "the test"; } } { package SubClass; use base qw( BaseClass ); use Test::More; sub setup : Tests( setup => +1 ) { my $self = shift; $self->SUPER::setup; pass "subclass setup"; } } Test::Class->runtests; __END__ prove -v foo.t # # SubClass->test 1..5 ok 1 - base setup ok 2 - subclass setup ok 3 - the test # # BaseClass->test ok 4 - base setup ok 5 - the test ok 81 ms
    Update: you can get the superclass setup methods to be called if if you don't override them by providing a subclass method of the same name, but then they're called in alphabetical order. As I've painfully discovered, that breaks things if your subclass setup method relies on something your superclass setup method creates.

    I'm possible being dim - but I'm not sure what you're getting at here.

      (setup => +1) syntax solves everything quite nicely. Thanks!

      As for the second thing, if a parent and derived class both have setup methods but the parent setup methods are not overridden in the child, the calling order should be parent methods first, not alphabetical first. Otherwise, when a subclasses' setup method just happens to have a test name that's alphabetically prior to parent classes' setup method names, the setup methods may very well get called in an inappropriate order.

      Cheers,
      Ovid

      New address of my CGI Course.

        Check. Already on the todo list as the rather opaque "Use JUnit 4.0's inheritance model for before/after running order?" :-)