NAME

Test::Log::Abstraction - Capture log output in tests and assert on it

VERSION

0.001.0

SYNOPSIS

use Test::Most;
use Test::Log::Abstraction;

my $logger = Test::Log::Abstraction->new();
my $obj = Some::Class->new(logger => $logger);

$obj->do_something();

# Assertions on what was logged
$logger->like(qr/updated/, 'do_something() logs that it updated');
$logger->has_level('error');
$logger->unlike(qr/fatal/);
$logger->count() == 3;
$logger->clear();

# Or simply see the messages
diag($_) foreach @{ $logger->messages() };

DESCRIPTION

A test double for Log::Abstraction, drop-in wherever code under test is passed a logger => object.

Every level method that Log::Abstraction offers (trace, debug, info, notice, warn, error, critical, alert, emergency and their syslog aliases) records the message instead of writing it to a file, and optionally sends it to TAP diagnostics. Nothing is ever written to disk, and no logging backend is loaded.

Diagnostics

Messages at warning and above are printed with "diag" in Test::Builder by default, so a test that accidentally triggers a warning is visible; trace, debug, info and notice are printed only in verbose mode. Verbose mode is on when verbose => 1 is passed to new() or $ENV{TEST_VERBOSE} is true.

Change it with the diag option: 'all' prints everything, 'none' prints nothing (unless verbose), a level name such as 'error' prints that level and everything more severe, and an array reference prints just those levels.

Migrating from t/lib/MyLogger.pm

Replace, in each test file:

use lib 't/lib';
use MyLogger;
...
logger => MyLogger->new()

with:

use Test::Log::Abstraction;
...
logger => Test::Log::Abstraction->new()

and delete t/lib/MyLogger.pm. Unlike the old MyLogger copies, this implementation is identical everywhere, never recurses when a level method is called with undef (see t/autoload.t), and records every message so tests can assert on it instead of only printing it.

METHODS

new

my $logger = Test::Log::Abstraction->new();
my $logger = Test::Log::Abstraction->new(verbose => 1, diag => 'none');

Takes optional verbose and diag options (see "DESCRIPTION"); any other arguments are accepted and ignored, as "new" in Log::Abstraction allows a configuration hash to be passed through. Called on an existing logger it makes a clone with the same options and a copy of the captured messages, as "new" in Log::Abstraction does.

messages

my $arrayref = $logger->messages();

Array reference of { level, message } hash references, in the order they were logged. Entries logged with Log::Abstraction's structured fields also carry a fields hash reference.

clear

$logger->clear();

Empties the captured messages and returns the logger.

count

my $n = $logger->count();          # all messages
my $n = $logger->count('error');   # just one level

Number of captured messages, optionally restricted to one level.

like

$logger->like(qr/updated/, 'optional test name');

Passes if any captured message matches the pattern. Returns the result, and reports it as a test through Test::Builder, so count it in your plan (or use done_testing()).

unlike

$logger->unlike(qr/fatal/, 'optional test name');

Passes if no captured message matches the pattern.

has_level

$logger->has_level('error', 'optional test name');

Passes if at least one message was logged at that level.

empty

$logger->empty('nothing was logged');

Passes if nothing at all was captured - the usual assertion after a clean run.

verbose

my $verbose = $logger->verbose();
$logger->verbose(1);

Gets or sets verbose mode.

DIAGNOSTICS

no method 'foo' - the code under test called $logger->foo(), which is not a log level; the message is captured under that name and the notice is always printed, so a typo'd level cannot pass silently.

trace, debug, info, notice, warn, error, critical, alert, emergency

$logger->warn('something looks wrong');
$logger->info('started', { pid => $$ });
$logger->error({ error => 'cannot open file' });

Every Log::Abstraction level and syslog alias is a method. Each records the call and, subject to the diag setting, prints it. Arguments follow Log::Abstraction's rules: they are concatenated into the message, a hash reference at the end of two or more arguments is captured as structured fields, and a lone hash reference is the message and is rendered as key => value pairs so it can be matched. undef arguments become the string undef and never warn.

Levels are thin wrappers over _record; they exist so that AUTOLOAD only sees genuinely unknown methods.

  • trace, debug, info, informational, notice

    Captured; printed only in verbose mode by default.

  • warn, warning, error, err, critical, crit, fatal, alert, emergency, emerg, panic

    Captured and printed by default.

new

See "new" above.

AUTOLOAD

Any other method call - typically a level name that doesn't exist, such as a typo - captures the message under that name and prints a notice, instead of dying part way through a test.

messages, clear, count, like, unlike, has_level, empty, verbose

See "METHODS" above.

SEE ALSO

Log::Abstraction, Test::Builder, Test::Most

AUTHOR

Nigel Horne, <njh at nigelhorne.com>

LICENCE AND COPYRIGHT

Copyright 2026 Nigel Horne.

Usage is subject to the GPL2 licence terms. If you use it, please let me know.