Ovid's post on avoiding
Test::More's use_ok() is good advice. There's almost no reason to use
use_ok()
from Test::More in existing
code. It probably doesn't do what you think it does, and it doesn't really help
against most of the failures you probably care about.
Worse, it can give you a false sense of security and mislead you into debugging the wrong thing.
Why? Because it turns what ought to be a fatal, program-killing exceptional condition ("Hey, I couldn't load this module! I'd better stop now. Things are certainly not going to work the way anyone expects!") into a simple failed test ("Oopsie! Better check your assumptions! I'll keep going though, because hopefully you just made a typo in your test assertion!").
The problem really isn't with use_ok()
though. The problem's
with Perl 5's require.
require
does one thing. It searches the filesystem for the
named file, compiles it, and caches the success or failure of compilation.
(Make that three things.)
Here's the problem: what if compilation fails? What of compilation fails halfway through the file? What if compilation fails on the very last line of the file because the module doesn't return a true value? Try it yourself:
package FalseReturnValue;
sub demo { 'demo' }
sub demo2 { 2 }
0;
... and the test:
use Test::More;
use lib 'lib';
use_ok( 'FalseReturnValue' );
is FalseReturnValue::demo(), 'demo', 'Declared functions exist';
is FalseReturnValue::demo2(), 2, '... all of them';
done_testing;
The problem is that failing to load a module should never leave your system in an inconsistent state.
Getting this right is very, very difficult. Getting this right means not
committing anything to globally visible symbols until you're certain that the
module compiled correctly. For a module like FalseReturnValue
,
that's easy. For a module which itself uses something like Catalyst or
DBIx::Class with several dependencies, this is tricky.
The best approach I can think of is to maintain some sort of transactional system (yes, I know it sounds awfully complex, but you asked for correctness first, so humor me through at least this sentence) where you build up a set of changes to globally visible symbols and then only apply that delta if that compilation as a whole—the top-level module and all of its dependencies—succeeds.
The second best solution is to do that for each module. It's all or nothing
for each use
statement on its own, regardless of how far down the
dependency tree you are.
(You could go one step further and make everything anonymous by default, such that the only way you can access package global symbols in another namespace is by binding to that namespace explicitly, but that's a bigger change with implications on code reuse and the cross cutting concerns of an object system, even though it does have the potential to clean up things like accidental exports.)
Of course, if your worldview has already said that failing to load a
dependency with use
should abort the program with red flashing
klaxon lights and a siren, you don't have to do that much work.
(... but require
errors are exceptions you can catch with
eval { ... }
, so the problem remains with
require
.)
While I agree that a require failure leaving things in a weird state is bad... the connection to `use_ok` is a stretch. The "early failures make later failures suspect" is a known trade off in Perl testing. You'd be better off rewriting the article just to talk about the `require` problem without the `use_ok` distraction.
I use 'use_ok' in a test file such as t/000_sanity.t to tell me if all my XS-based modules can be found and loaded correctly in Math::GSL. It is quite useful for that.
I agree with Schwern, 'use_ok' is not the problem.
I see this most in conjunction with use_ok, because it's compilation errors introduced while developing that cause the most weird failures.
leto:
Really? What does it achieve that a “use” wouldn’t?
use_ok( $module_name ) is in some ways prettier than eval "use $module_name", and it gives diagnostics on success. Beyond that, there's little else.