File Coverage

blib/lib/Test/Magpie.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1 6     6   171320 use strict;
  6         14  
  6         234  
2 6     6   31 use warnings;
  6         11  
  6         298  
3             package Test::Magpie;
4             {
5             $Test::Magpie::VERSION = '0.11';
6             }
7             # ABSTRACT: Mocking framework with method stubs and behaviour verification
8              
9              
10 6     6   5067 use aliased 'Test::Magpie::Inspect';
  6         4561  
  6         31  
11             use aliased 'Test::Magpie::Mock';
12             use aliased 'Test::Magpie::Verify';
13             use aliased 'Test::Magpie::When';
14              
15             use Carp qw( croak );
16             use Exporter qw( import );
17             use Scalar::Util qw( looks_like_number );
18             use Test::Magpie::Types 'NumRange', Mock => { -as => 'MockType' };
19              
20              
21             our @EXPORT = qw(
22             mock
23             when
24             verify
25             );
26             our @EXPORT_OK = qw(
27             at_least
28             at_most
29             inspect
30             );
31              
32              
33             sub mock {
34             return Mock->new if @_ == 0;
35              
36             my ($class) = @_;
37              
38             croak 'The argument for mock() must be a string'
39             unless !ref $class;
40              
41             return Mock->new(class => $class);
42             }
43              
44              
45             sub when {
46             my ($mock) = @_;
47              
48             croak 'when() must be given a mock object'
49             unless defined $mock && MockType->check($mock);
50              
51             return When->new(mock => $mock);
52             }
53              
54              
55             sub verify {
56             my $mock = shift;
57             my $test_name;
58             $test_name = pop if (@_ % 2 == 1);
59             my %options = @_;
60              
61             # set default option if none given
62             $options{times} = 1 if keys %options == 0;
63              
64             croak 'verify() must be given a mock object'
65             unless defined $mock && MockType->check($mock);
66              
67             croak 'You can set only one of these options: '
68             . join ', ', map {"'$_'"} keys %options
69             unless keys %options == 1;
70              
71             if (defined $options{times}) {
72             croak "'times' option must be a number" unless (
73             looks_like_number $options{times} ||
74             ref $options{times} eq 'CODE'
75             );
76             }
77             elsif (defined $options{at_least}) {
78             croak "'at_least' option must be a number"
79             unless looks_like_number $options{at_least};
80             }
81             elsif (defined $options{at_most}) {
82             croak "'at_most' option must be a number"
83             unless looks_like_number $options{at_most};
84             }
85             elsif (defined $options{between}) {
86             croak "'between' option must be an arrayref "
87             . "with 2 numbers in ascending order" unless (
88             NumRange->check( $options{between} ) &&
89             $options{between}[0] < $options{between}[1]
90             );
91             }
92              
93             # set test name if given
94             $options{test_name} = $test_name if defined $test_name;
95              
96             return Verify->new(mock => $mock, %options);
97             }
98              
99              
100             sub inspect {
101             my ($mock) = @_;
102              
103             croak 'inspect() must be given a mock object'
104             unless defined $mock && MockType->check($mock);
105              
106             return Inspect->new(mock => $mock);
107             }
108              
109              
110             sub at_least {
111             warnings::warnif('deprecated', 'at_least() is deprecated');
112              
113             my ($n) = @_;
114             croak "at_least() must be given a number"
115             unless ! defined $n || looks_like_number $n;
116              
117             return sub {
118             my ($invocations, $called, $test_name, $tb) = @_;
119              
120             $test_name = sprintf '%s was called at least %u time(s)', $called, $n
121             unless defined $test_name;
122              
123             $tb->cmp_ok($invocations, '>=', $n, $test_name);
124             }
125             }
126              
127              
128             sub at_most {
129             warnings::warnif('deprecated', 'at_most() is deprecated');
130              
131             my ($n) = @_;
132             croak "at_most() must be given a number"
133             unless ! defined $n || looks_like_number $n;
134              
135             return sub {
136             my ($invocations, $called, $test_name, $tb) = @_;
137              
138             $test_name = sprintf '%s was called at most %u time(s)', $called, $n
139             unless defined $test_name;
140              
141             $tb->cmp_ok($invocations, '<=', $n, $test_name);
142             }
143             }
144              
145             1;
146              
147             __END__
148              
149             =pod
150              
151             =encoding utf-8
152              
153             =head1 NAME
154              
155             Test::Magpie - Mocking framework with method stubs and behaviour verification
156              
157             =head1 SYNOPSIS
158              
159             use Test::Magpie;
160              
161             # create the mock object and stub
162             my $baker = mock;
163             when($mock)->bake_loaf('white')->then_return($bread);
164              
165             # execute the code under test
166             my $bakery = Bakery->new( bakers => [ $baker ] );
167             my @loaves = $bakery->buy_loaf( amount => 2, type => 'white' );
168              
169             # verify the interactions with the mock object
170             verify($baker, times => 2)->bake_loaf('white');
171              
172             =head1 DESCRIPTION
173              
174             Test::Magpie is a test double framework heavily inspired by the Mockito
175             framework for Java, and also the Python-Mockito project. In Mockito, you "spy"
176             on objects for their behaviour, rather than being upfront about what should
177             happen. I find this approach to be significantly more flexible and easier to
178             work with than mocking systems like EasyMock, so I created a Perl
179             implementation.
180              
181             =over 4
182              
183             =item Mock objects
184              
185             Mock objects, represented by L<Test::Magpie::Mock> objects, are objects that
186             pretend to be everything you could ever want them to be. A mock object can have
187             any method called on it, does every roles, and isa subclass of any superclass.
188             This allows you to easily throw a mock object around it will be treated as
189             though it was a real object.
190              
191             =item Method stubbing
192              
193             Any method can be called on a mock object, and it will be logged as an
194             invocation. By default, method calls return C<undef> in scalar context or an
195             empty list in list context. Often, though, clients will be interested in the
196             result of calling a method with some arguments. So you may specify how a
197             method stub should respond when it is called.
198              
199             =item Verify interactions
200              
201             After calling your concrete code (the code under test) you may want to check
202             that the code did operate correctly on the mock. To do this, you can use
203             verifications to make sure code was called, with correct parameters and the
204             correct amount of times.
205              
206             =item Argument matching
207              
208             Magpie gives you some helpful methods to validate arguments passed in to calls.
209             You can check equality between arguments, or consume a general type of argument,
210             or consume multiple arguments. See L<Test::Magpie::ArgumentMatcher> for the
211             juicy details.
212              
213             =back
214              
215             =head1 FUNCTIONS
216              
217             =head2 mock
218              
219             C<mock()> constructs a new instance of a mock object.
220              
221             $mock = mock;
222             $mock->method(@args);
223              
224             C<$class> is an optional argument to set the type that the mock object is
225             blessed into. This value will be returned when C<ref()> is called on the object.
226              
227             $mock = mock($class);
228             is( ref($mock), $class );
229              
230             =head2 when
231              
232             C<when()> is used to tell the method stub to return some value(s) or to raise
233             an exception.
234              
235             when($mock)->method(@args)->then_return(1, 2, 3);
236             when($mock)->invalid(@args)->then_die('exception');
237              
238             =head2 verify
239              
240             C<verify()> is used to check the interactions on your mock object and prints
241             the test result. C<verify()> plays nicely with L<Test::Simple> and Co - it
242             depends on them for setting a test plan and its calls are counted in the test
243             plan.
244              
245             verify($mock)->method(@args)
246             # prints: ok 1 - method("foo") was called 1 time(s)
247              
248             C<verify()> accepts an optional C<$test_name> to print a custom name for the
249             test instead of the default.
250              
251             verify($mock, $test_name)->method(@args)
252             # prints: ok 1 - record inserted into database'
253              
254             C<verify()> accepts a few options to help your verifications:
255              
256             verify( $mock, times => 3, )->method(@args)
257             verify( $mock, at_least => 3 )->method(@args)
258             verify( $mock, at_most => 5 )->method(@args)
259             verify( $mock, between => [3, 5] )->method(@args)
260              
261             =over 4
262              
263             =item times
264              
265             Specifies the number of times the given method is expected to be called. The
266             default is 1 if no other option is specified.
267              
268             =item at_least
269              
270             Specifies the minimum number of times the given method is expected to be
271             called.
272              
273             =item at_most
274              
275             Specifies the maximum number of times the given method is expected to be
276             called.
277              
278             =item between
279              
280             Specifies the minimum and maximum number of times the given method is expected
281             to be called.
282              
283             =back
284              
285             A C<$test_name> may also be supplied after the option.
286              
287             verify($mock, times => 3, $test_name)->method(@args)
288              
289             =head2 inspect
290              
291             Inspect method invocations on a mock object.
292              
293             $invocation = inspect($mock)->method(@args);
294             is( $invocation->method_name, 'foo' );
295             is_deeply( [$invocation->arguments], [qw( bar baz )] );
296              
297             =head2 at_least (deprecated)
298              
299             Used with C<verify()> to verify that a method was invoked at least C<$n> times.
300              
301             verify($mock, times => at_least($n))->method(@args);
302              
303             This function has been deprecated. Use the C<at_least> option for C<verify()>
304             instead.
305              
306             =head2 at_most (deprecated)
307              
308             Used with C<verify()> to verify that a method was invoked at most C<$n> times.
309              
310             verify($mock, times => at_most($n))->method(@args);
311              
312             This function has been deprecated. Use the C<at_most> option for C<verify()>
313             instead.
314              
315             =head1 EXPORTS
316              
317             This module exports the following functions by default:
318              
319             =over 4
320              
321             =item *
322              
323             mock
324              
325             =item *
326              
327             when
328              
329             =item *
330              
331             verify
332              
333             =back
334              
335             All other functions need to be imported explicitly.
336              
337             =head1 AUTHORS
338              
339             =over 4
340              
341             =item *
342              
343             Oliver Charles <oliver.g.charles@googlemail.com>
344              
345             =item *
346              
347             Steven Lee <stevenwh.lee@gmail.com>
348              
349             =back
350              
351             =head1 COPYRIGHT AND LICENSE
352              
353             This software is copyright (c) 2013 by Oliver Charles.
354              
355             This is free software; you can redistribute it and/or modify it under
356             the same terms as the Perl 5 programming language system itself.
357              
358             =cut