File Coverage

blib/lib/Mock/Quick.pm
Criterion Covered Total %
statement 61 62 98.3
branch 6 6 100.0
condition n/a
subroutine 20 21 95.2
pod 0 1 0.0
total 87 90 96.6


line stmt bran cond sub pod time code
1             package Mock::Quick;
2 3     3   167159 use strict;
  3         8  
  3         105  
3 3     3   17 use warnings;
  3         7  
  3         85  
4 3     3   3148 use Exporter::Declare;
  3         113345  
  3         18  
5 3     3   7635 use Mock::Quick::Class;
  3         8  
  3         106  
6 3     3   2156 use Mock::Quick::Object;
  3         9  
  3         68  
7 3     3   16 use Mock::Quick::Object::Control;
  3         4  
  3         51  
8 3     3   16 use Mock::Quick::Method;
  3         5  
  3         52  
9 3     3   14 use Mock::Quick::Util;
  3         6  
  3         229  
10 3     3   16 use Carp qw/carp/;
  3         13  
  3         369  
11              
12             our $VERSION = '1.108';
13              
14             import_arguments qw/intercept/;
15              
16             sub after_import {
17 3     3 0 12303 my $class = shift;
18 3         7 my ( $importer, $specs ) = @_;
19              
20 3 100       13 return unless $specs->config->{intercept};
21              
22 1         11 my $intercept = $specs->config->{intercept};
23 3     3   26 no strict 'refs';
  3         7  
  3         2270  
24 1     1   7 *{"$importer\::QINTERCEPT"} = sub { $intercept };
  1         2311  
  1         4  
25             }
26              
27             my %CLASS_RELATED = (
28             qclass => 'new',
29             qtakeover => 'takeover',
30             qimplement => 'implement',
31             );
32              
33             for my $operation ( keys %CLASS_RELATED ) {
34             my $meth = $CLASS_RELATED{$operation};
35              
36             default_export $operation => sub {
37 7     7   1925 my @args = @_;
38              
39 7 100       58 return Mock::Quick::Class->$meth(@args)
40             if defined wantarray;
41              
42 2         11 my $caller = caller;
43 1     1   1339 return $caller->QINTERCEPT->(sub { Mock::Quick::Class->$meth(@args) })
44 2 100       58 if $caller->can( 'QINTERCEPT' );
45              
46 1         12 carp "Return value is ignored, your mock is destroyed as soon as it is created.";
47             };
48             }
49              
50 0     0   0 default_export qcontrol => sub { Mock::Quick::Object::Control->new(@_) };
51              
52             default_export qobj => sub {
53 2     2   15 my $obj = Mock::Quick::Object->new(@_);
54 2         13 my $control = Mock::Quick::Object::Control->new($obj);
55 2         8 $control->strict(0);
56 2         12 return $obj;
57             };
58              
59             default_export qobjc => sub {
60 3     3   31 my $obj = Mock::Quick::Object->new(@_);
61 3         25 my $control = Mock::Quick::Object::Control->new($obj);
62 3         16 $control->strict(0);
63 3         10 return ( $obj, $control );
64             };
65              
66             default_export qstrict => sub {
67 1     1   6 my $obj = Mock::Quick::Object->new(@_);
68 1         6 my $control = Mock::Quick::Object::Control->new($obj);
69 1         4 $control->strict(1);
70 1         4 return $obj;
71             };
72              
73             default_export qstrictc => sub {
74 1     1   6 my $obj = Mock::Quick::Object->new(@_);
75 1         5 my $control = Mock::Quick::Object::Control->new($obj);
76 1         5 $control->strict(1);
77 1         4 return ( $obj, $control );
78             };
79              
80 4     4   1584 default_export qclear => sub { \$Mock::Quick::Util::CLEAR };
81 5     5   1008 default_export qmeth => sub(&) { Mock::Quick::Method->new(@_) };
82              
83             purge_util();
84              
85             1;
86              
87             __END__
88              
89             =pod
90              
91             =head1 NAME
92              
93             Mock::Quick - Quickly mock objects and classes, even temporarily replace them,
94             side-effect free.
95              
96             =head1 DESCRIPTION
97              
98             Mock-Quick is here to solve the current problems with Mocking libraries.
99              
100             There are a couple Mocking libraries available on CPAN. The primary problems
101             with these libraries include verbose syntax, and most importantly side-effects.
102             Some Mocking libraries expect you to mock a specific class, and will unload it
103             then redefine it. This is particularly a problem if you only want to override
104             a class on a lexical level.
105              
106             Mock-Object provides a declarative mocking interface that results in a very
107             concise, but clear syntax. There are separate facilities for mocking object
108             instances, and classes. You can quickly create an instance of an object with
109             custom attributes and methods. You can also quickly create an anonymous class,
110             optionally inheriting from another, with whatever methods you desire.
111              
112             Mock-Object also provides a tool that provides an OO interface to overriding
113             methods in existing classes. This tool also allows for the restoration of the
114             original class methods. Best of all this is a localized tool, when your control
115             object falls out of scope the original class is restored.
116              
117             =head1 SYNOPSIS
118              
119             =head2 MOCKING OBJECTS
120              
121             use Mock::Quick;
122              
123             my $obj = qobj(
124             foo => 'bar', # define attribute
125             do_it => qmeth { ... }, # define method
126             ...
127             );
128              
129             is( $obj->foo, 'bar' );
130             $obj->foo( 'baz' );
131             is( $obj->foo, 'baz' );
132              
133             $obj->do_it();
134              
135             # define the new attribute automatically
136             $obj->bar( 'xxx' );
137              
138             # define a new method on the fly
139             $obj->baz( qmeth { ... });
140              
141             # remove an attribute or method
142             $obj->baz( qclear() );
143              
144             =head2 STRICTER MOCK
145              
146             use Mock::Quick;
147              
148             my $obj = qstrict(
149             foo => 'bar', # define attribute
150             do_it => qmeth { ... }, # define method
151             ...
152             );
153              
154             is( $obj->foo, 'bar' );
155             $obj->foo( 'baz' );
156             is( $obj->foo, 'baz' );
157              
158             $obj->do_it();
159              
160             # remove an attribute or method
161             $obj->baz( qclear() );
162              
163             You can no longer auto-vivify accessors and methods in strict mode:
164              
165             # Cannot define the new attribute automatically
166             dies_ok { $obj->bar( 'xxx' ) };
167              
168             # Cannot define a new method on the fly
169             dies_ok { $obj->baz( qmeth { ... }) };
170              
171             In order to add methods/accessors you need to create a control object.
172              
173             =head2 CONTROL OBJECTS
174              
175             Control objects are objects that let you interface a mocked object. They let
176             you add attributes and methods, or even clear them. This is unnecessary unless
177             you use strict mocking, or choose not to import qmeth() and qclear().
178              
179             =over 4
180              
181             =item Take Control
182              
183             my $control = qcontrol( $obj );
184              
185             =item Add Attributes
186              
187             $control->set_attributes(
188             foo => 'bar',
189             ...
190             );
191              
192             =item Add Methods
193              
194             $control->set_methods(
195             do_it => sub { ... }, # No need to use qmeth()
196             ...
197             );
198              
199             =item Clear Attributes/Methods
200              
201             $control->clear( qw/foo do_it .../ );
202              
203             =item Toggle strict
204              
205             $control->strict( $BOOL );
206              
207             =item Create With Control
208              
209             my $obj = qobj ...;
210             my $obj = qstrict ...;
211             my ( $obj, $control ) = qobjc ...;
212             my ( $sobj, $scontrol ) = qstrictc ...;
213              
214             =back
215              
216             =head2 MOCKING CLASSES
217              
218             B<Note:> the control object returned here is of type L<Mock::Quick::Class>,
219             whereas control objects for qobj style objects are of
220             L<Mock::Quick::Object::Control>.
221              
222             =head3 IMPLEMENT A CLASS
223              
224             This will implement a class at the namespace provided via the -implement
225             argument. The class must not already be loaded. Once complete the real class
226             will be prevented from loading until you call undefine() on the control object.
227              
228             use Mock::Quick;
229              
230             my $control = qclass(
231             -implement => 'My::Package',
232              
233             # Insert a generic new() method (blessed hash)
234             -with_new => 1,
235              
236             # Inheritance
237             -subclass => 'Some::Class',
238             # Can also do
239             -subclass => [ 'Class::A', 'Class::B' ],
240              
241             # generic get/set attribute methods.
242             -attributes => [ qw/a b c d/ ],
243              
244             # Method that simply returns a value.
245             simple => 'value',
246              
247             # Custom method.
248             method => sub { ... },
249             );
250              
251             my $obj = $control->package->new;
252             # OR
253             my $obj = My::Package->new;
254              
255             # Override a method
256             $control->override( foo => sub { ... });
257              
258             # Restore it to the original
259             $control->restore( 'foo' );
260              
261             # Remove the namespace we created, which would allow the real thing to load
262             # in a require or use statement.
263             $control->undefine();
264              
265             You can also use the qimplement() method instead of qclass:
266              
267             use Mock::Quick;
268              
269             my $control = qimplement 'Some::Package' => ( %args );
270              
271             =head3 ANONYMOUS MOCKED CLASS
272              
273             This is if you just need to generate a class where the package name does not
274             matter. This is done when the -takeover and -implement arguments are both
275             omitted.
276              
277             use Mock::Quick;
278              
279             my $control = qclass(
280             # Insert a generic new() method (blessed hash)
281             -with_new => 1,
282              
283             # Inheritance
284             -subclass => 'Some::Class',
285             # Can also do
286             -subclass => [ 'Class::A', 'Class::B' ],
287              
288             # generic get/set attribute methods.
289             -attributes => [ qw/a b c d/ ],
290              
291             # Method that simply returns a value.
292             simple => 'value',
293              
294             # Custom method.
295             method => sub { ... },
296             );
297              
298             my $obj = $control->package->new;
299              
300             # Override a method
301             $control->override( foo => sub { ... });
302              
303             # Restore it to the original
304             $control->restore( 'foo' );
305              
306             # Remove the anonymous namespace we created.
307             $control->undefine();
308              
309             =head3 TAKING OVER EXISTING/LOADED CLASSES
310              
311             use Mock::Quick;
312              
313             my $control = qtakeover 'Some::Package' => ( %overrides );
314              
315             # Override a method
316             $control->override( foo => sub { ... });
317              
318             # Restore it to the original
319             $control->restore( 'foo' );
320              
321             # Destroy the control object and completely restore the original class
322             # Some::Package.
323             $control = undef;
324              
325             You can also do this through qclass():
326              
327             use Mock::Quick;
328              
329             my $control = qclass(
330             -takeover => 'Some::Package',
331             %overrides
332             );
333              
334             =head1 METRICS
335              
336             All control objects have a 'metrics' method. The metrics method returns a hash
337             where keys are method names, and values are the number of times the method has
338             been called. When a method is altered or removed the key is deleted.
339              
340             Metrics only apply to mocked methods. When you takeover an already loaded class
341             metrics will only track overridden methods.
342              
343             =head1 EXPORTS
344              
345             Mock-Quick uses L<Exporter::Declare>. This allows for exports to be prefixed or renamed.
346             See L<Exporter::Declare/RENAMING IMPORTED ITEMS> for more information.
347              
348             =over 4
349              
350             =item $obj = qobj( attribute => value, ... )
351              
352             =item ( $obj, $control ) = qobjc( attribute => value, ... )
353              
354             Create an object. Every possible attribute works fine as a get/set accessor.
355             You can define other methods using qmeth {...} and assigning that to an
356             attribute. You can clear a method using qclear() as an argument.
357              
358             See L<Mock::Quick::Object> for more.
359              
360             =item $obj = qstrict( attribute => value, ... )
361              
362             =item ( $obj, $control ) = qstrictc( attribute => value, ... )
363              
364             Create a stricter object, get/set accessors will not autovivify into existence
365             for undefined attributes.
366              
367             =item $control = qclass( -config => ..., name => $value || sub { ... }, ... )
368              
369             Define an anonymous package with the desired methods and specifications.
370              
371             See L<Mock::Quick::Class> for more.
372              
373             =item $control = qclass( -takeover => $package, %overrides )
374              
375             =item $control = qtakeover( $package, %overrides );
376              
377             Take over an existing class.
378              
379             See L<Mock::Quick::Class> for more.
380              
381             =item $control = qimplement( $package, -config => ..., name => $value || sub { ... }, ... )
382              
383             =item $control = qclass( -implement => $package, ... )
384              
385             Implement the given package to specifications, altering %INC so that the real
386             class will not load. Destroying the control object will once again allow the
387             original to load.
388              
389             =item qclear()
390              
391             Returns a special reference that when used as an argument, will cause
392             Mock::Quick::Object methods to be cleared.
393              
394             =item qmeth { my $self = shift; ... }
395              
396             Define a method for an L<Mock::Quick::Object> instance.
397              
398             default_export qcontrol => sub { Mock::Quick::Object::Control->new( @_ ) };
399              
400              
401             =back
402              
403             =head1 AUTHORS
404              
405             Chad Granum L<exodist7@gmail.com>
406              
407             Ben Hengst L<notbenh@cpan.org>
408              
409             =head1 CONTRIBUTORS
410              
411             Contributors are listed as authors in modules they have touched.
412              
413             =over 4
414              
415             =item Ben Hengst L<notbenh@cpan.org>
416              
417             =item Glen Hinkle L<glen@empireenterprises.com>
418              
419             =back
420              
421             =head1 COPYRIGHT
422              
423             Copyright (C) 2011 Chad Granum
424              
425             Mock-Quick is free software; Standard perl licence.
426              
427             Mock-Quick is distributed in the hope that it will be useful, but WITHOUT ANY
428             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
429             PARTICULAR PURPOSE. See the license for more details.