NAME
Test::Mockingbird::DeepMock - Declarative structured mocking and spying for Perl tests
VERSION
Version 0.12
SYNOPSIS
use Test::Mockingbird::DeepMock qw(deep_mock);
deep_mock(
{
mocks => [
{ target => 'MyApp::greet', type => 'mock', with => sub { 'hi' } },
{ target => 'MyApp::double', type => 'spy', tag => 'double_spy' },
],
expectations => [
{ tag => 'double_spy', calls => 2 },
],
},
sub {
is MyApp::greet(), 'hi', 'greet mocked';
MyApp::double(2);
MyApp::double(3);
}
);
DESCRIPTION
Test::Mockingbird::DeepMock provides a declarative, data-driven way to describe mocking, spying, injection, and expectations in Perl tests.
Instead of scattering mock, spy, and restore_all calls throughout your test code, DeepMock lets you define a complete mocking plan in a single hashref, then executes your test code under that plan.
LIMITATIONS
- Nested deep_mock scopes are not supported
-
deep_mockcallsrestore_all()on exit, which removes every active mock. A nesteddeep_mockcall will cause the inner exit to also tear down the outer mocks. - inject type in mocks list
-
The
injectmock type delegates toTest::Mockingbird::inject(). Becauseinject()builds a sub that returns thewithvalue verbatim, passing a coderef viawithstores the coderef itself; it is not called on each access. To inject a factory, wrap it explicitly.
PLAN STRUCTURE
mocks
ArrayRef of mock specs. Each entry:
{
target => 'Package::method',
type => 'mock' | 'spy' | 'inject',
with => sub { ... },
tag => 'identifier',
}
expectations
ArrayRef of expectation specs. Each entry:
{
tag => 'spy_tag',
calls => $n,
args_like => [ [qr/pat1/, qr/pat2/], ... ],
args_eq => [ ['exact', 'args'], ... ],
args_deeply => [ [$struct], ... ],
never => 1,
order => [ 'A::m1', 'B::m2' ],
}
The order key is processed after all per-spy expectations and does not require a tag.
globals
Optional:
globals => { restore_on_scope_exit => 1 }
time
Optional time-travel plan applied before mocks are installed:
time => {
freeze => '2025-01-01T00:00:00Z',
travel => '2025-01-03T00:00:00Z',
advance => [ 2 => 'minutes' ],
rewind => [ 1 => 'hour' ],
}
TROUBLESHOOTING
"Not enough arguments for deep_mock"
DeepMock uses ($$) prototype. Use deep_mock( {...}, sub { ... } ).
My mocks are not restored
Check globals = { restore_on_scope_exit => 0 }> has not been set.
METHODS
deep_mock
Run a code block with a set of mocks and expectations applied.
API SPECIFICATION
Input (Params::Validate::Strict schema)
$plan -- HashRef with keys: mocks, expectations, globals, time
$code -- CodeRef
Output (Returns::Set schema)
returns: whatever $code returns (context-sensitive)
MESSAGES
"deep_mock expects a HASHREF plan" -- first arg is not a hashref
FORMAL SPECIFICATION
deep_mock ≙
∀ plan : HashRef; code : CodeRef •
pre ref(plan) = 'HASH'
post apply_time(plan.time)
∧ install_mocks(plan.mocks)
∧ result = code()
∧ check_expectations(plan.expectations)
∧ restore_all()
PSEUDOCODE
validate plan is HASH
apply time plan if present
install each mock/spy/inject from plan.mocks, building %handles
capture wantarray context
eval { run $code }
run expectations against %handles
restore all mocks (unless restore_on_scope_exit => 0)
restore time state
re-croak any exception from $code
return $code's result in correct context
SUPPORT
Please report bugs at https://github.com/nigelhorne/Test-Mockingbird/issues.
AUTHOR
Nigel Horne, <njh at nigelhorne.com>
SEE ALSO
REPOSITORY
https://github.com/nigelhorne/Test-Mockingbird
LICENCE AND COPYRIGHT
Copyright 2026 Nigel Horne.
Usage is subject to GPL2 licence terms.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 155:
Non-ASCII character seen before =encoding in '≙'. Assuming UTF-8