File Coverage

blib/lib/Test/Mock/Class/Role/Meta/Class.pm
Criterion Covered Total %
statement 70 83 84.3
branch 13 24 54.1
condition n/a
subroutine 127 129 98.4
pod 4 4 100.0
total 214 240 89.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -c
2              
3             package Test::Mock::Class::Role::Meta::Class;
4              
5             =head1 NAME
6              
7             Test::Mock::Class::Role::Meta::Class - Metaclass for mock class
8              
9             =head1 DESCRIPTION
10              
11             This role provides an API for defining and changing behavior of mock class.
12              
13             =cut
14              
15 1     1   953 use 5.006;
  1         4  
  1         43  
16              
17 1     1   7 use strict;
  1         2  
  1         37  
18 1     1   6 use warnings;
  1         2  
  1         54  
19              
20             our $VERSION = '0.0303';
21              
22 1     1   8 use Moose::Role;
  1         3  
  1         9  
23              
24              
25 1     1   6688 use Moose::Util;
  1         3  
  1         7  
26              
27 1     1   173 use Symbol ();
  1         3  
  1         21  
28              
29 1     1   6 use Test::Assert ':all';
  1         2  
  1         10  
30              
31             use Exception::Base (
32 1         13 '+ignore_package' => [__PACKAGE__],
33 1     1   1262 );
  1         3  
34              
35              
36             =head1 ATTRIBUTES
37              
38             =over
39              
40             =item B<mock_base_object_role> : Str = "Test::Mock::Class::Role::Object"
41              
42             Base object role for mock class. The default is
43             L<Test::Mock::Class::Role::Object>.
44              
45             =cut
46              
47             has 'mock_base_object_role' => (
48             is => 'rw',
49             default => 'Test::Mock::Class::Role::Object',
50             );
51              
52             =item B<mock_ignore_methods_regexp> : RegexpRef = "/^(_?mock_|(can|DEMOLISHALL|DESTROY|DOES|does|isa|VERSION)$)/"
53              
54             Regexp matches method names which are not created automatically for mock
55             class.
56              
57             =cut
58              
59             has 'mock_ignore_methods_regexp' => (
60             is => 'rw',
61             default => sub { qr/^(_?mock_|(can|DEMOLISHALL|DESTROY|DOES|does|meta|isa|VERSION)$)/ },
62             );
63              
64             =item B<mock_constructor_methods_regexp> : RegexpRef = "/^new$/"
65              
66             Regexp matches method names which are constructors rather than normal methods.
67              
68             =back
69              
70             =cut
71              
72             has 'mock_constructor_methods_regexp' => (
73             is => 'rw',
74             default => sub { qr/^new$/ },
75             );
76              
77              
78 1     1   374 use namespace::clean -except => 'meta';
  1         3  
  1         11  
79              
80              
81             ## no critic qw(RequireCheckingReturnValueOfEval)
82              
83             =head1 CONSTRUCTORS
84              
85             =over
86              
87             =item B<create_mock_class>( I<name> : Str, :I<class> : Str, I<args> : Hash ) : Moose::Meta::Class
88              
89             Creates new L<Moose::Meta::Class> object which represents named mock class.
90             It automatically adds all methods which exists in original class, except those
91             which matches C<mock_ignore_methods_regexp> attribute.
92              
93             If C<new> method exists in original class, it is created as constructor.
94              
95             The method takes additional arguments:
96              
97             =over
98              
99             =item class
100              
101             Optional I<class> parameter is a name of original class and its methods will
102             be created for new mock class.
103              
104             =item methods
105              
106             List of additional methods to create.
107              
108             =back
109              
110             The constructor returns metaclass object.
111              
112             Test::Mock::Class->create_mock_class(
113             'IO::File::Mock' => ( class => 'IO::File' )
114             );
115              
116             =cut
117              
118             sub create_mock_class {
119 4     4 1 1270 my ($class, $name, %args) = @_;
120 4         32 my $self = $class->create($name, %args);
121 4         4823 $self->_construct_mock_class(%args);
122 4         114 return $self;
123             };
124              
125              
126             =item B<create_mock_anon_class>( :I<class> : Str, I<args> : Hash ) : Moose::Meta::Class
127              
128             Creates new L<Moose::Meta::Class> object which represents anonymous mock
129             class. Optional I<class> parameter is a name of original class and its
130             methods will be created for new mock class.
131              
132             Anonymous classes are destroyed once the metaclass they are attached to goes
133             out of scope.
134              
135             The constructor returns metaclass object.
136              
137             my $meta = Test::Mock::Class->create_mock_anon_class(
138             class => 'File::Temp'
139             );
140              
141             =back
142              
143             =cut
144              
145             sub create_mock_anon_class {
146 51     51 1 1371415 my ($class, %args) = @_;
147 51         339 my $self = $class->create_anon_class(%args);
148 51         71743 $self->_construct_mock_class(%args);
149 51         1975 return $self;
150             };
151              
152              
153             =head1 METHODS
154              
155             =over
156              
157             =item B<add_mock_method>( I<method> : Str ) : Self
158              
159             Adds new I<method> to mock class. The behavior of this method can be changed
160             with C<mock_return> and other methods.
161              
162             =cut
163              
164             sub add_mock_method {
165 412     412 1 1721 my ($self, $method) = @_;
166             $self->add_method( $method => sub {
167 142     142   53609 my $method_self = shift;
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
        142      
168 142         572 return $method_self->mock_invoke($method, @_);
169 412         2417 } );
170 412         19961 return $self;
171             };
172              
173              
174             =item B<add_mock_constructor>( I<method> : Str ) : Self
175              
176             Adds new constructor to mock class. This is almost the same as
177             C<add_mock_method> but it returns new object rather than defined value.
178              
179             The calls counter is set to C<1> for new object's constructor.
180              
181             =cut
182              
183             sub add_mock_constructor {
184 56     56 1 1501 my ($self, $constructor) = @_;
185             $self->add_method( $constructor => sub {
186 2     2   57 my $method_class = shift;
        2      
        2      
        2      
        2      
        2      
        2      
        2      
187 2 100       49 $method_class->mock_invoke($constructor, @_) if blessed $method_class;
188 2         14 my $new_object = $method_class->meta->new_object(@_);
189 2         17 $new_object->mock_invoke($constructor, @_);
190 2         8 return $new_object;
191 56         457 } );
192 56         2956 return $self;
193             };
194              
195              
196             =item B<_construct_mock_class>( :I<class> : Str, :I<methods> : ArrayRef ) : Self
197              
198             Constructs mock class based on original class. Adds the same methods as in
199             original class. If original class has C<new> method, the constructor with
200             this name is created.
201              
202             =cut
203              
204             sub _construct_mock_class {
205 55     55   181 my ($self, %args) = @_;
206              
207 55         2673 Moose::Util::apply_all_roles(
208             $self,
209             $self->mock_base_object_role,
210             );
211              
212 55         253775 $self->superclasses( $self->_get_mock_superclasses($args{class}) );
213              
214 55 50       130929 my @methods = defined $args{methods} ? @{ $args{methods} } : ();
  0         0  
215              
216 55         106 my @mock_methods = do {
217 55         266 my %uniq = map { $_ => 1 }
  2006         51180  
218             (
219             $self->get_all_method_names,
220             @methods,
221             );
222 55         1242 keys %uniq;
223             };
224              
225 55         296 foreach my $method (@mock_methods) {
226 2006 100       87964 if ($method =~ $self->mock_ignore_methods_regexp) {
    100          
227             # ignore destructor and basic instrospection methods
228             }
229             elsif ($method =~ $self->mock_constructor_methods_regexp) {
230 55         236 $self->add_mock_constructor($method);
231             }
232             else {
233 411         1097 $self->add_mock_method($method);
234             };
235             };
236              
237 55         399 return $self;
238             };
239              
240              
241             sub _get_mock_superclasses {
242 55     55   189 my ($self, $class) = @_;
243              
244 55 100       265 return ('Moose::Object') unless defined $class;
245              
246 3         18 my @superclasses = (
247             $class->can('meta')
248             ? $class->meta->superclasses
249 54 100       899 : @{ *{Symbol::qualify_to_ref('ISA', $class)} },
  3         6  
250             );
251              
252 57         277 unshift @superclasses, 'Moose::Object'
253 54 100       4882 unless grep { $_ eq 'Moose::Object' } @superclasses;
254              
255 54         141 unshift @superclasses, $class;
256              
257 54         309 return @superclasses;
258             };
259              
260              
261             sub _get_mock_metaclasses {
262 0     0     my ($self, $class) = @_;
263              
264 0 0         return () unless defined $class;
265 0 0         return () unless $class->can('meta');
266              
267             return (
268 0           attribute_metaclass => $class->meta->attribute_metaclass,
269             instance_metaclass => $class->meta->instance_metaclass,
270             method_metaclass => $class->meta->method_metaclass,
271             );
272             };
273              
274              
275             sub _get_mock_metaclass_instance_roles {
276 0     0     my ($self, $class) = @_;
277              
278 0 0         return () unless defined $class;
279 0 0         return () unless $class->can('meta');
280              
281 0           my $metaclass_instance = $class->meta->get_meta_instance->meta;
282              
283 0 0         return () unless $metaclass_instance->can('roles');
284              
285 0           return map { $_->name }
  0            
286 0           @{ $metaclass_instance->roles };
287             };
288              
289              
290             1;
291              
292              
293             =back
294              
295             =begin umlwiki
296              
297             = Class Diagram =
298              
299             [ <<role>>
300             Test::Mock::Class::Role::Meta::Class
301             ------------------------------------------------------------------------------
302             +mock_base_object_role = "Test::Mock::Class::Role::Object"
303             +mock_ignore_methods_regexp : RegexpRef = "/^(can|DEMOLISHALL|DESTROY|DOES|does|isa|VERSION)$/"
304             +mock_constructor_methods_regexp : RegexpRef = "/^new$/"
305             ------------------------------------------------------------------------------
306             +create_mock_class( name : Str, :class : Str, args : Hash ) : Moose::Meta::Class
307             +create_mock_anon_class( :class : Str, args : Hash ) : Moose::Meta::Class
308             +add_mock_method( method : Str ) : Self
309             +add_mock_constructor( method : Str ) : Self
310             ]
311              
312             =end umlwiki
313              
314             =head1 SEE ALSO
315              
316             L<Test::Mock::Class>.
317              
318             =head1 BUGS
319              
320             The API is not stable yet and can be changed in future.
321              
322             =head1 AUTHOR
323              
324             Piotr Roszatycki <dexter@cpan.org>
325              
326             =head1 LICENSE
327              
328             Based on SimpleTest, an open source unit test framework for the PHP
329             programming language, created by Marcus Baker, Jason Sweat, Travis Swicegood,
330             Perrick Penet and Edward Z. Yang.
331              
332             Copyright (c) 2009, 2010 Piotr Roszatycki <dexter@cpan.org>.
333              
334             This program is free software; you can redistribute it and/or modify it
335             under GNU Lesser General Public License.