Changes for version 0.10 - 2026-05-08

  • Bug fixes
  • _parse_target() used !defined $arg3 to detect the single-argument shorthand form ('Pkg::method'). Because no caller ever passes three arguments to _parse_target(), $arg3 is always undef, making the guard permanently true. As a result, spy('A::B', 'method') was misread as shorthand and resolved to package 'A', method 'B' -- silently spying on the wrong target. Fixed by changing the discriminator to !defined $arg2: the shorthand form has exactly one argument (arg2 undef), the longhand form has two (arg2 defined). All other callers pass a single string argument and are unaffected.
  • mock() emitted "Prototype mismatch: sub ... ()" warnings whenever it replaced a function that carried a Perl prototype (such as the () no-args prototype used by I18N::LangTags::Detect::detect). The warning fired because the replacement coderef had no prototype, so Perl flagged the redefinition as a signature mismatch. Fixed by calling Scalar::Util::set_prototype on the replacement immediately before installing it, copying the prototype from the original coderef. The call uses the & sigil (&Scalar::Util::set_prototype) to bypass set_prototype's own (&$) prototype constraint, which would otherwise reject a lexical variable as the first argument. unmock() required no change: reinstating the original coderef via glob assignment restores its prototype automatically.
  • Scalar::Util is now loaded (use Scalar::Util ()) without importing set_prototype into the Test::Mockingbird namespace, avoiding any risk of the imported alias inheriting the (&$) prototype constraint at the call site.
  • Notes
  • spy() installs its wrapper coderef directly without going through mock(), so it does not benefit from the set_prototype fix and still emits a prototype-mismatch warning when wrapping a prototyped function. This is a known limitation documented in the test suite and will be addressed separately.
  • Tests
  • Added prototype-preservation subtests to all four test files:
    • unit.t Six white-box subtests confirming that prototype() on the installed glob matches the original after mock() for (), ($$), ($), and no-prototype functions, including across stacked mocks and after unmock.
    • function.t Three subtests capturing $SIG{__WARN__} and asserting no prototype-mismatch warning is emitted during the mock/unmock cycle for (), ($$), and no-prototype functions. Return-value assertions on () functions use ->can() to bypass Perl's compile-time constant inlining of () prototype functions.
    • integration.t Three end-to-end subtests covering plain mock(), deep_mock(), and mock_scoped() on a () prototype function. All use ->can() closures for return-value assertions; the plain mock() subtest uses the fully-qualified Test::Mockingbird::restore_all() to avoid shadowing by the TimeTravel restore_all import.
    • edge-cases.t Five boundary-condition subtests: () warning suppression; stacked mocks each independently carrying the prototype; mock_scoped() delegating correctly to mock(); spy() documented as a known limitation that still emits the mismatch warning; and ($$) full cycle.

Documentation

Modules

Advanced mocking library for Perl with support for dependency injection and spies
Declarative, structured mocking and spying for Perl tests
Deterministic, controllable time for Perl tests

Provides

in lib/Test/Mockingbird.pm