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.