2

With Test::More I often want to have a module that runs tests and has the ability to abort the callers test_plan. I have a series of tests that set up a plugin list for Catalyst::Test. I don't want to have to make my test check to see if they exist; instead, I want my script to abort if those plugins aren't present.

I was trying to track down a bug in my Catalyst::Authentication::Store::DBI::ButMaintained, and I noticed this bug is also present in Catalyst::Authentication::Store::DBI. Here it is:

eval {
  require Catalyst::Model::DBI;
  require Catalyst::Plugin::Session;
  require Catalyst::Plugin::Session::State::Cookie;
  require Catalyst::Plugin::Session::Store::File;
  require DBD::SQLite;
  require Test::WWW::Mechanize::Catalyst;
} or plan skip_all => $@;

...

$ENV{'TESTAPP_PLUGINS'} = [ qw(
  Authentication
  Session
  Session::Store::File
  Session::State::Cookie
  Authorization::Roles
) ];

As you can see, the eval/skip_all doesn't check Authorization::Roles inclusion, but the test depends on it by virtue of it being a plugin.

I have another question though -- is there a more elegant way to specify Test-dependencies than this? Keep in mind my goal is the same as the original authors. I simply want to skip the test, if the test requirements don't exist. Ideally, in this case, I'd like to hack Catalyst::Test to wrap the plugin mechanism for Catalyst::Plugin::* stuff, and then find a better way to do the rest of this stuff without eval/skip_all.

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468

2 Answers2

1

You can dynamically affect the # of tests in the plan by calling:

Test::More->builder->plan(tests=>$total_tests);

You can use that do conditionally calculate the # of tests based on the needs.

DVK
  • 126,886
  • 32
  • 213
  • 327
1

Update with your list of plugins as needed:

If you are testing a bunch of requirements in a separate package, you could simply have that package return false (rather than the traditional true value) if a dependency is not met:

package Catalyst::Test;

eval {
    use Dep1;
    use Dep2;
    # ...
}

# dep check package returns true if we found all the modules
!$@;

# test.pl
use Test::Requires {
    Catalyst::Test => 0.01,  # skip all tests if Catalyst::Test is not present
};
use Test::More tests => 20;  # call Test::More as normal.

When I run this using a dep checker called Foo, this fails with appropriate output:

% perl -I. foo.t
1..0 # SKIP Foo.pm did not return a true value at (eval 4) line 2.
# BEGIN failed--compilation aborted at (eval 4) line 2.
#
% prove -I. foo.t
foo.pl .. skipped: Foo.pm did not return a true value at (eval 4) line 2.
Files=1, Tests=0,  0 wallclock secs ( 0.02 usr  0.01 sys +  0.01 cusr  0.00 csys =  0.04 CPU)
Result: NOTESTS
Ether
  • 53,118
  • 13
  • 86
  • 159
  • I wouldn't object to this being present in my test script. But, `Catalyst::Test` should still test its own plugin list, and the script should skip if those plugins aren't present. – Evan Carroll Sep 07 '10 at 19:27
  • @Evan: updated; the module referenced in Test::Requires can simply return false if it is unhappy with the environment. – Ether Sep 07 '10 at 19:34
  • Again, I'm not sure returning non-true is the reason why I want my scripts to SKIP, I'd rather them be much more verbose. I want the Catalyst Plugins ideally to cause the calling test to skip if they're not installed -- this should be a CORE feature of `Catalyst::Test` imho, and I'm interested in patching it. The rest of what I'm looking for seems to already be present in `Test::Requires`. – Evan Carroll Sep 07 '10 at 19:38
  • @Evan: ok I think I see what you're going for... perhaps simply putting the Test::Requires check inside Catalyst::Test would be sufficient? It might be best to verify that you're "in" a test first - perhaps simply checking for Test::Builder in %INC would be enough. – Ether Sep 07 '10 at 20:51