1Mock::MonkeyPatch(3pm)User Contributed Perl DocumentationMock::MonkeyPatch(3pm)
2
3
4
6 Mock::MonkeyPatch - Monkey patching with test mocking in mind
7
9 {
10 package MyApp;
11
12 sub gen_item_id {
13 my $type = shift;
14 # calls external service and gets id for $type
15 }
16
17 sub build_item {
18 my $type = shift;
19 my $item = Item->new(type => $type);
20 $item->id(gen_item_id($type));
21 return $item;
22 }
23 }
24
25 use Test::More;
26 use MyApp;
27 use Mock::MonkeyPatch;
28
29 my $mock = Mock::MonkeyPatch->patch(
30 'MyApp::gen_item_id' => sub { 'abcd' }
31 );
32
33 my $item = MyApp::build_item('rubber_chicken');
34 is $item->id, 'abcd', 'building item calls MyApp::gen_random_id';
35 ok $mock->called, 'the mock was indeed called';
36 is_deeply $mock->arguments, ['rubber_chicken'], 'the mock was called with expected arguments';
37
39 Mocking is a common tool, especially for testing. By strategically
40 replacing a subroutine, one can isolate segments (units) of code to
41 test individually. When this is done it is important to know that the
42 mocked sub was actually called and with what arguments it was called.
43
44 Mock::MonkeyPatch injects a subroutine in the place of an existing one.
45 It returns an object by which you can revisit the manner in which the
46 mocked subroutine was called. Further when the object goes out of
47 scope (or when the "restore" method is called) the original subroutine
48 is replaced.
49
51 patch
52 my $mock = Mock::MonkeyPatch->patch('MyPackage::foo' => sub { ... });
53 my $mock = Mock::MonkeyPatch->patch('MyPackage::foo' => sub { ... }, \%options);
54
55 Mock a subroutine and return a object to represent it. Takes a fully
56 qualifed subroutine name, a subroutine reference to call in its place,
57 and optionally a hash reference of additional constructor arguments.
58
59 The replacement subroutine will be wrapped in a one that will store
60 calling data, then injected in place of the original. Within the
61 replacement subroutine the original is available as the fully qualified
62 subroutine "Mock::MonkeyPatch::ORIGINAL". This can be used to inject
63 behavior before, after, or even around the original. This includes
64 munging the arguments passed to the origial (though the actual
65 arguments are what are stored). For example usage, see "COOKBOOK".
66
67 Currently the optional hashref only accepts one option, an initial
68 value for "store_arguments" which is true if not given.
69
70 The wrapper will have the same prototype as the mocked function if one
71 exists. The replacement need not have any prototype, the arguments
72 received by the wrapper will be passed to the given sub as they were
73 received. (If this doesn't make any sense to you, don't worry about
74 it.)
75
77 arguments
78 my $args = $mock->arguments;
79 my $args_second_time = $mock->arguments(1);
80
81 Returns an array reference containing the arguments that were passed to
82 the mocked subroutine (but see also "store_arguments"). Optionally an
83 integer may be passed which designates the call number to fetch
84 arguments in the same manner of indexing an array (zero indexed). If
85 not given, 0 is assumed, representing the first time the mock was
86 called. Returns "undef" if the mocked subroutine was not called (or
87 was not called enough times).
88
89 use Test::More;
90 is_deeply $mock->arguments, [1, 2, 3], 'called with the right arguments';
91
92 called
93 my $time_called = $mock->called;
94
95 Returns the number of times the mocked subroutine was called. This
96 means that that there should be values available from "arguments" up to
97 the value of "$mock->called - 1".
98
99 use Test::More;
100 ok $mock->called, 'mock was called';
101 is $mock->called, 3, 'mock was called three times';
102
103 method_arguments
104 my $args = $mock->method_arguments;
105 my $args_third_time = $mock->method_arguments(2, 'MyClass');
106
107 A wrapper around "arguments" convenient for when the mocked subroutine
108 is called as a method. Like "arguments" it returns a subroutine
109 reference, though it removes the first arguments which is the invocant.
110 It also can take a call number designation.
111
112 Additionally it takes a class name to test against the invocant as
113 "$invocant->isa('Class::Name')". If the invocant is not an instance of
114 the class or a subclass thereof it returns "undef".
115
116 use Test::More;
117 is_deeply $mock->method_arguments(0, 'FrobberCo::Employee'),
118 ['some', 'arguments'], 'mock method called with known arguments on a FrobberCo::Employee instance';
119
120 reset
121 $mock = $mock->reset;
122
123 Reset the historical information stored in the mock, including
124 "arguments" and "called". Returns the mock instance for chaining if
125 desired.
126
127 Note that this does not restore the original method. for that, see
128 "restore".
129
130 use Test::More;
131 is $mock->called, 3, 'called 3 times';
132 is $mock->reset->called, 0, 'called zero times after reset';
133
134 restore
135 $mock = $mock->restore;
136
137 Restore the original method to its original place in the symbol table.
138 This method is also called automatically when the object goes out of
139 scope and is garbage collected. Returns the mock instance for chaining
140 if desired. This method can only be called once!
141
142 Note that this does not reset historical information stored in the
143 mock, for that, see "reset".
144
145 store_arguments
146 $mock = $mock->store_arguments(0);
147
148 When true, the default if not passed to the constructor, arguments
149 passed to the mocked subroutine are stored and accessible later via
150 "arguments" and "method_arguments". However sometimes this isn't
151 desirable, especially in cases where the reference count of items in
152 the arguments matter; notably when an object should be destroyed and
153 the destructor's behavior is important. When this is true set
154 "store_arguments" to a false value and only an empty array reference
155 will be stored.
156
157 When used as a setter, it returns the mock instance for chaining if
158 desired.
159
161 Run code before the original
162 The original version of the mocked function (read: the code that was
163 available via the symbol at the time the mock was initiated) is
164 available via the fully qualified symbol "Mock::MonkeyPatch::ORIGINAL".
165 You can call this in your mock if for example you want to do some setup
166 before calling the function.
167
168 my $mock = $self->patch($symbol, sub {
169 # do some stuff before the original
170 do_mocked_stuff(@_);
171 # then call the original function/method
172 Mock::MonkeyPatch::ORIGINAL(@_);
173 });
174
175 Using ORIGINAL in a nonblocking environment
176 Since the "ORIGINAL" symbol is implemented via "local" if you want to
177 call it after leaving the scope you need to store a reference to the
178 function in a lexical.
179
180 my $mock = $self->patch($symbol, sub {
181 my @args = @_;
182 my $orig = \&Mock::MonkeyPatch::ORIGINAL;
183 Mojo::IOLoop->timer(1 => sub { $orig->(@args) });
184 });
185
187 • Test::MockObject
188
189 • Mock::Quick
190
191 • Mock::Sub
192
194 <http://github.com/jberger/Mock-MonkeyPatch>
195
197 Joel Berger, <joel.a.berger@gmail.com>
198
200 • Doug Bell (preaction)
201
202 • Brian Medley (bpmedley)
203
205 Copyright (C) 2016 by Joel Berger and "CONTRIBUTORS"
206
207 This library is free software; you can redistribute it and/or modify it
208 under the same terms as Perl itself.
209
210
211
212perl v5.36.0 2022-07-22 Mock::MonkeyPatch(3pm)