File Coverage

blib/lib/Test/Mock/Class.pm
Criterion Covered Total %
statement 29 29 100.0
branch 3 4 75.0
condition n/a
subroutine 9 9 100.0
pod n/a
total 41 42 97.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -c
2              
3             package Test::Mock::Class;
4              
5             =head1 NAME
6              
7             Test::Mock::Class - Simulating other classes
8              
9             =head1 SYNOPSIS
10              
11             use Test::Mock::Class ':all';
12             require Net::FTP;
13              
14             # concrete mocked class
15             mock_class 'Net::FTP' => 'Net::FTP::Mock';
16             my $mock_object = Net::FTP::Mock->new;
17              
18             # anonymous mocked class
19             my $metamock = mock_anon_class 'Net::FTP';
20             my $mock_object = $metamock->new_object;
21              
22             # anonymous class with role applied
23             my $metamock = Test::Mock::Class->create_anon_class(
24             roles => [ 'My::Handler::Role' ],
25             );
26             my $mock_object = $metamock->new_object;
27              
28             =head1 DESCRIPTION
29              
30             In a unit test, mock objects can simulate the behavior of complex, real
31             (non-mock) objects and are therefore useful when a real object is impractical
32             or impossible to incorporate into a unit test.
33              
34             The unique features of C<Test::Mock::Class>:
35              
36             =over 2
37              
38             =item *
39              
40             Its API is inspired by PHP SimpleTest framework.
41              
42             =item *
43              
44             It isn't tied with L<Test::Builder> so it can be used standalone or with any
45             xUnit-like framework, i.e. L<Test::Unit::Lite>. Look for
46             L<Test::Builder::Mock::Class> if you want to use it with L<Test::Builder>
47             (L<Test::More> or L<Test::Simple>).
48              
49             =item *
50              
51             The API for creating mock classes is based on L<Moose> and L<Class::MOP> so it
52             doesn't clash with API of original class and is easy expandable.
53              
54             =item *
55              
56             The methods for defining mock object's behavior are prefixed with C<mock_>
57             string so they shouldn't clash with original object's methods.
58              
59             =item *
60              
61             Mocks as actors: The mock version of a class has all the methods of the
62             original class. The return value will be C<undef>, but it can be changed with
63             C<mock_returns> method.
64              
65             =item *
66              
67             Mocks as critics: The method of mock version of a class can check its calling
68             arguments and throws an exception if arguments don't match (C<mock_expect>
69             method). An exception also can be thrown if the method wasn't called at all
70             (C<mock_expect_once> method).
71              
72             =back
73              
74             =for readme stop
75              
76             =cut
77              
78 1     1   1706325 use 5.006;
  1         5  
  1         46  
79              
80 1     1   8 use strict;
  1         2  
  1         54  
81 1     1   7 use warnings;
  1         27  
  1         65  
82              
83             our $VERSION = '0.0303';
84              
85 1     1   7 use Moose 0.90;
  1         24  
  1         10  
86 1     1   8144 use Class::MOP 0.93;
  1         37  
  1         69  
87              
88              
89             =head1 INHERITANCE
90              
91             =over 2
92              
93             =item *
94              
95             extends L<Moose::Meta::Class>
96              
97             =cut
98              
99             extends 'Moose::Meta::Class';
100              
101             =item *
102              
103             with L<Test::Mock::Class::Role::Meta::Class>
104              
105             =back
106              
107             =cut
108              
109             with 'Test::Mock::Class::Role::Meta::Class';
110              
111              
112 1     1   7 use namespace::clean -except => 'meta';
  1         3  
  1         12  
113              
114              
115             =head1 FUNCTIONS
116              
117             =over
118              
119             =cut
120              
121             BEGIN {
122 1     1   608 my %exports = ();
123              
124             =item B<mock_class>( I<class> : Str, I<mock_class> : Str = undef ) : Moose::Meta::Class
125              
126             Creates the concrete mock class based on original I<class>. If the name of
127             I<mock_class> is undefined, its name is created based on name of original
128             I<class> with added C<::Mock> suffix.
129              
130             The original I<class> is loaded with C<L<Class::MOP>::load_class> function
131             which behaves wrongly for some packages, i.e. I<IO::File>. It is much safer
132             to require original class explicitly.
133              
134             The function returns the metaclass object of new I<mock_class>.
135              
136             =cut
137              
138             $exports{mock_class} = sub {
139             sub ($;$) {
140 3 100   3   2147 return __PACKAGE__->create_mock_class(
141             defined $_[1] ? $_[1] : $_[0] . '::Mock',
142             class => $_[0],
143             );
144 2         51 };
145 1         12 };
146              
147             =item B<mock_anon_class>( I<class> : Str = undef ) : Moose::Meta::Class
148              
149             Creates an anonymous mock class based on original I<class>. The name of this
150             class is automatically generated. If I<class> argument not defined, the empty
151             mock class is created.
152              
153             The function returns the metaobject of new mock class.
154              
155             =back
156              
157             =cut
158              
159             $exports{mock_anon_class} = sub {
160             sub (;$) {
161 2 50   2   1577 return __PACKAGE__->create_mock_anon_class(
162             defined $_[0] ? (class => $_[0]) : (),
163             );
164 2         4634911 };
165 1         4 };
166              
167             =head1 IMPORTS
168              
169             =over
170              
171             =cut
172              
173 1         2 my %groups = ();
174              
175             =item Test::Mock::Class ':all';
176              
177             Imports all functions into caller's namespace.
178              
179             =back
180              
181             =cut
182              
183 1         5 $groups{all} = [ keys %exports ];
184              
185 1         8 require Sub::Exporter;
186 1         12 Sub::Exporter->import(
187             -setup => {
188             exports => [ %exports ],
189             groups => \%groups,
190             },
191             );
192             };
193              
194              
195             1;
196              
197              
198             =begin umlwiki
199              
200             = Class Diagram =
201              
202             [ Test::Mock::Class
203             ----------------------------------------------------------------------------------
204             ----------------------------------------------------------------------------------
205             <<utility>> mock_class(class : Str, mock_class : Str = undef) : Moose::Meta::Class
206             <<utility>> mock_anon_class(class : Str) : Moose::Meta::Class
207             ]
208              
209             [Test::Mock::Class] ---|> [Moose::Meta::Class] [<<role>> Test::Mock::Class::Role::Meta::Class]
210              
211             =end umlwiki
212              
213             =head1 EXAMPLE
214              
215             The C<Test::Mock::Class> fits perfectly to L<Test::Unit::Lite> tests. It
216             throws an exception immediately if some problem is occurred. It means that
217             the test unit is failed if i.e. the mock method is called with wrong
218             arguments.
219              
220             Example code:
221              
222             package My::ExampleTest;
223              
224             use Test::Unit::Lite;
225              
226             use Moose;
227             extends 'Test::Unit::TestCase';
228              
229             use Test::Assert ':all';
230             use Test::Mock::Class ':all';
231              
232             require IO::File;
233              
234             sub test_mock_class {
235             my ($self) = @_;
236              
237             my $mock = mock_anon_class 'IO::File';
238             my $io = $mock->new_object;
239             $io->mock_return( open => 1, args => [qr//, 'r'] );
240              
241             assert_true( $io->open('/etc/passwd', 'r') );
242              
243             $io->mock_tally;
244             };
245              
246             =head1 SEE ALSO
247              
248             Mock metaclass API: L<Test::Mock::Class::Role::Meta::Class>,
249             L<Moose::Meta::Class>.
250              
251             Mock object methods: L<Test::Mock::Class::Role::Object>.
252              
253             xUnit-like testing: L<Test::Unit::Lite>.
254              
255             Mock classes for L<Test::Builder>: L<Test::Builder::Mock::Class>.
256              
257             Other implementations: L<Test::MockObject>, L<Test::MockClass>.
258              
259             =for readme continue
260              
261             =head1 BUGS
262              
263             The API is not stable yet and can be changed in future.
264              
265             =head1 AUTHOR
266              
267             Piotr Roszatycki <dexter@cpan.org>
268              
269             =head1 LICENSE
270              
271             Based on SimpleTest, an open source unit test framework for the PHP
272             programming language, created by Marcus Baker, Jason Sweat, Travis Swicegood,
273             Perrick Penet and Edward Z. Yang.
274              
275             Copyright (c) 2009, 2010 Piotr Roszatycki <dexter@cpan.org>.
276              
277             This program is free software; you can redistribute it and/or modify it
278             under GNU Lesser General Public License.