Mock::Sub(3pm)

πŸ“¦
version 1.09 Β· 4 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– Mock::Sub πŸ’–

okay bestie, so this is Mock::Sub β€” the Perl manual page for it, and honestly? it's a whole vibe. it's a mock package, object and standard subroutines situation, made with unit testing in mind. πŸŽ€


✨ NAME ✨

Mock::Sub β€” mock package, object and standard subroutines, with unit testing in mind. no cap. πŸ’…


🌸 SYNOPSIS 🌸

# see EXAMPLES for a full use case and caveats

use Mock::Sub;

# disable warnings about mocking non-existent subs

use Mock::Sub no_warnings => 1

# create the parent mock object

my $mock = Mock::Sub->new;

# mock some subs...

my $foo = $mock->mock('Package::foo');
my $bar = $mock->mock('Package::bar');

# wait until a mocked sub is called

Package::foo();

# then...

$foo->name;         # name of sub that's mocked
$foo->called;       # was the sub called?
$foo->called_count; # how many times was it called?
$foo->called_with;  # array of params sent to sub

# have the mocked sub return something when it's called (list or scalar).

$foo->return_value(1, 2, {a => 1});
my @return = Package::foo;

# have the mocked sub perform an action

$foo->side_effect( sub { die "eval catch" if @_; } );

eval { Package::foo(1); };
like ($@, qr/eval catch/, "side_effect worked with params");

# extract the parameters the sub was called with (if return_value or
# side_effect is not used, we will return the parameters that were sent into
# the mocked sub (list or scalar context)

my @args = $foo->called_with;

# reset the mock object for re-use within the same scope

$foo->reset;

# restore original functionality to the sub

$foo->unmock;

# re-mock a previously unmock()ed sub

$foo->remock;

# check if a sub is mocked

my $state = $foo->mocked_state;

# mock out a CORE:: function. Be warned that this *must* be done within
# compile stage (BEGIN), and the function can NOT be unmocked prior
# to the completion of program execution

my ($mock, $caller);

BEGIN {
    $mock = Mock::Sub->new;
    $caller = $mock->mock('caller');
};

$caller->return_value(55);
caller(); # mocked caller() called

πŸ¦‹ DESCRIPTION πŸ¦‹

so like, Mock::Sub is an easy to use and very lightweight module for mocking out sub calls. it's super useful for testing areas of your own modules where getting coverage may be difficult because there's literally nothing to test against, and/or for reducing test run time by eliminating the need to call subs that you really don't want or need to test. it's giving "skip the whole thing but still get your green checkmarks". periodt. πŸ’…


🌈 EXAMPLE 🌈

here's a full example to get further coverage where it's difficult if not impossible to test certain areas of your code (eg: you have if/else statements, but they don't do anything but call other subs. You don't want to test the subs that are called, nor do you want to add statements to your code).

ngl, important note: if the end subroutine you're testing is NOT Object Oriented (and you're importing them into your module that you're testing), you have to mock them as part of your own namespace (ie. instead of Other::first, you'd mock MyModule::first). iykyk.

# module you're testing:

package MyPackage;

use Other;
use Exporter qw(import);
@EXPORT_OK = qw(test);

my $other = Other->new;

sub test {
    my $arg = shift;

    if ($arg == 1){
        # how do you test this?... there's no return etc.
        $other->first();
    }
    if ($arg == 2){
        $other->second();
    }
}

# your test file

use MyPackage qw(test);
use Mock::Sub;
use Test::More tests => 2;

my $mock = Mock::Sub->new;

my $first = $mock->mock('Other::first');
my $second = $mock->mock('Other::second');

# coverage for first if() in MyPackage::test
test(1);
is ($first->called, 1, "1st if() statement covered");

# coverage for second if()
test(2);
is ($second->called, 1, "2nd if() statement covered");

πŸ‘‘ MOCK OBJECT METHODS πŸ‘‘

🌟 new(%opts)

instantiates and returns a new Mock::Sub object, ready to be used to start creating mocked sub objects. it's basically the parent of the whole situation. 🫢

optional options, bestie:

  • return_value => $scalar β€” set this to have all mocked subs created with this mock object return anything you wish (accepts a single scalar only. See return_value() method to return a list and for further information). you can also set it in individual mocks only (see return_value() method).
  • side_effect => $cref β€” set this in new() to have the side effect passed into all child mocks created with this object. See side_effect() method.

🌟 mock('sub', %opts)

instantiates and returns a new mock object on each call. 'sub' is the name of the subroutine to mock (requires full package name if the sub isn't in main::).

the mocked sub will return the parameters sent into the mocked sub if a return value isn't set, or a side effect doesn't return anything, if available. if you're in scalar context but a list was sent in, we'll return the first parameter in the list. in list context, we simply receive the parameters as they were sent in. no thoughts, just params. ✨

optional parameters:

see new() for a description of the parameters. both the return_value and side_effect parameters can be set in this method to individualize each mock object, and will override the global configuration if set in new().

there's also return_value() and side_effect() methods if you want to set, change or remove these values after instantiation of a child sub object.

🌟 mocked_subs

returns a list of all the names of the subs that are currently mocked under the parent mock object. the whole roster. πŸ’–

🌟 mocked_objects

returns a list of all sub objects underneath the parent mock object, regardless if its sub is currently mocked or not.

🌟 mocked_state('Sub::Name')

returns 1 if the sub currently under the parent mock object is mocked or not, and 0 if not. croaks if there hasn't been a child sub object created with this sub name. πŸ’€


πŸŽ€ SUB OBJECT METHODS πŸŽ€

these methods are for the children mocked sub objects returned from the parent mock object. see MOCK OBJECT METHODS for methods related to the parent mock object.

🌟 unmock

restores the original functionality back to the sub, and runs reset() on the object.

🌟 remock

re-mocks the sub within the object after calling unmock on it (accepts the side_effect and return_value parameters).

🌟 called

returns true (1) if the sub being mocked has been called, and false (0) if not.

🌟 called_count

returns the number of times the mocked sub has been called.

🌟 called_with

returns an array of the parameters sent to the subroutine. confess()s if we're called before the mocked sub has been called. 😭

🌟 mocked_state

returns true (1) if the sub the object refers to is currently mocked, and false (0) if not.

🌟 name

returns the name of the sub being mocked.

🌟 side_effect($cref)

add (or change/delete) a side effect after instantiation.

send in a code reference containing an action you'd like the mocked sub to perform.

the side effect function will receive all parameters sent into the mocked sub.

you can use both side_effect() and return_value() params at the same time. side_effect will be run first, and then return_value. note that if side_effect's last expression evaluates to any value whatsoever (even false), it will return that and return_value will be skipped. πŸ™ˆ

to work around this and have the side_effect run but still get the return_value thereafter, write your cref to evaluate undef as the last thing it does: sub { ...; undef; }.

🌟 return_value

add (or change/delete) the mocked sub's return value after instantiation. can be a scalar or list. send in undef to remove previously set values.

🌟 reset

resets the functional parameters (return_value, side_effect), along with called() and called_count() back to undef/false. does not restore the sub back to its original state.


🧚 NOTES 🧚

this module has a backwards parent-child relationship. to use, you create a mock object using MOCK OBJECT METHODS new and mock methods, thereafter, you use the returned mocked sub object SUB OBJECT METHODS to perform the work.

the parent mock object retains certain information and statistics of the child mocked objects (and the subs themselves).

to mock CORE::GLOBAL functions, you must initiate within a BEGIN block (see SYNOPSIS for details). it is important that if you mock a CORE sub, it can't and won't be returned to its original state until after the entire program process tree exists. Period. πŸ’…

i didn't make this a Test:: module (although it started that way) because i can see more uses than placing it into that category.


⭐ AUTHOR ⭐

Steve Bertrand, <steveb at cpan.org> β€” king behavior honestly πŸ‘‘


πŸ’€ BUGS πŸ’€

please report any bugs or requests at <https://github.com/stevieb9/mock-sub/issues>


πŸ“ REPOSITORY πŸ“

<https://github.com/stevieb9/mock-sub>


πŸ”₯ BUILD RESULTS πŸ”₯

CPAN Testers: <http://matrix.cpantesters.org/?dist=Mock-Sub>


🌈 SUPPORT 🌈

you can find documentation for this module with the perldoc command. bestie, it's right there:

perldoc Mock::Sub

πŸ’œ ACKNOWLEDGEMENTS πŸ’œ

Python's MagicMock module. respect where respect is due 🫢


Copyright 2016 Steve Bertrand.

this program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See <http://dev.perl.org/licenses/> for more information.


aaaand that's the whole page, bestie ✨ this was perl v5.34.0, dated 2022-03-06, and you just read Mock::Sub(3pm). slay, go mock something. πŸ’–πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from