line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package MooseX::Test::Role; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.07'; |
4
|
|
|
|
|
|
|
|
5
|
7
|
|
|
7
|
|
595374
|
use strict; |
|
7
|
|
|
|
|
26
|
|
|
7
|
|
|
|
|
266
|
|
6
|
7
|
|
|
7
|
|
55
|
use warnings; |
|
7
|
|
|
|
|
22
|
|
|
7
|
|
|
|
|
288
|
|
7
|
|
|
|
|
|
|
|
8
|
7
|
|
|
7
|
|
58
|
use Carp qw( confess ); |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
456
|
|
9
|
7
|
|
|
7
|
|
792
|
use Class::Load qw( try_load_class ); |
|
7
|
|
|
|
|
44445
|
|
|
7
|
|
|
|
|
442
|
|
10
|
7
|
|
|
7
|
|
61
|
use List::Util qw( first ); |
|
7
|
|
|
|
|
31
|
|
|
7
|
|
|
|
|
797
|
|
11
|
7
|
|
|
7
|
|
60
|
use Test::Builder; |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
278
|
|
12
|
|
|
|
|
|
|
|
13
|
7
|
|
|
7
|
|
123
|
use Exporter qw( import unimport ); |
|
7
|
|
|
|
|
23
|
|
|
7
|
|
|
|
|
8865
|
|
14
|
|
|
|
|
|
|
our @EXPORT = qw( requires_ok consumer_of consuming_object consuming_class ); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub requires_ok { |
17
|
1
|
|
|
1
|
1
|
31476
|
my ( $role, @required ) = @_; |
18
|
1
|
|
|
|
|
10
|
my $msg = "$role requires " . join( ', ', @required ); |
19
|
|
|
|
|
|
|
|
20
|
1
|
|
|
|
|
8
|
my $role_type = _derive_role_type($role); |
21
|
0
|
0
|
|
|
|
0
|
if (!$role_type) { |
22
|
0
|
|
|
|
|
0
|
ok( 0, $msg ); |
23
|
0
|
|
|
|
|
0
|
return; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
0
|
|
|
|
|
0
|
foreach my $req (@required) { |
27
|
0
|
0
|
|
0
|
|
0
|
unless ( first { $_ eq $req } _required_methods($role_type, $role) ) { |
|
0
|
|
|
|
|
0
|
|
28
|
0
|
|
|
|
|
0
|
ok( 0, $msg ); |
29
|
0
|
|
|
|
|
0
|
return; |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
} |
32
|
0
|
|
|
|
|
0
|
ok( 1, $msg ); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub consuming_class { |
36
|
3
|
|
|
3
|
1
|
66964
|
my ( $role, %args ) = @_; |
37
|
|
|
|
|
|
|
|
38
|
3
|
50
|
|
|
|
19
|
my %methods = exists $args{methods} ? %{ $args{methods} } : (); |
|
0
|
|
|
|
|
0
|
|
39
|
|
|
|
|
|
|
|
40
|
3
|
|
|
|
|
16
|
my $role_type = _derive_role_type($role); |
41
|
0
|
0
|
|
|
|
0
|
confess 'first argument should be a role' unless $role_type; |
42
|
|
|
|
|
|
|
|
43
|
0
|
|
|
|
|
0
|
my $package = _package_name(); |
44
|
0
|
|
|
|
|
0
|
_add_methods( |
45
|
|
|
|
|
|
|
package => $package, |
46
|
|
|
|
|
|
|
role_type => $role_type, |
47
|
|
|
|
|
|
|
role => $role, |
48
|
|
|
|
|
|
|
methods => \%methods, |
49
|
|
|
|
|
|
|
); |
50
|
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
0
|
_apply_role( |
52
|
|
|
|
|
|
|
package => $package, |
53
|
|
|
|
|
|
|
role_type => $role_type, |
54
|
|
|
|
|
|
|
role => $role, |
55
|
|
|
|
|
|
|
); |
56
|
|
|
|
|
|
|
|
57
|
0
|
|
|
|
|
0
|
return $package; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub consuming_object { |
61
|
1
|
|
|
1
|
1
|
18819
|
my $class = consuming_class(@_); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# Moose and Moo can be instantiated and should be. Role::Tiny however isn't |
64
|
|
|
|
|
|
|
# a full OO implementation and so doesn't provide a "new" method. |
65
|
0
|
0
|
|
|
|
0
|
return $class->can('new') ? $class->new() : $class; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub consumer_of { |
69
|
1
|
|
|
1
|
1
|
26004
|
my ( $role, %methods ) = @_; |
70
|
|
|
|
|
|
|
|
71
|
1
|
0
|
|
|
|
6
|
confess 'first argument to consumer_of should be a role' unless _derive_role_type($role); |
72
|
|
|
|
|
|
|
|
73
|
0
|
|
|
|
|
0
|
return consuming_object( $role, methods => \%methods ); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub _required_methods { |
77
|
0
|
|
|
0
|
|
0
|
my ($role_type, $role) = @_; |
78
|
0
|
|
|
|
|
0
|
my @methods; |
79
|
|
|
|
|
|
|
|
80
|
0
|
0
|
|
|
|
0
|
if ($role_type eq 'Moose::Role') { |
|
|
0
|
|
|
|
|
|
81
|
0
|
|
|
|
|
0
|
@methods = $role->meta->get_required_method_list(); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
elsif ($role_type eq 'Role::Tiny') { |
84
|
0
|
|
|
|
|
0
|
my $info = _role_tiny_info($role); |
85
|
0
|
0
|
0
|
|
|
0
|
if ($info && ref($info->{requires}) eq 'ARRAY') { |
86
|
0
|
|
|
|
|
0
|
@methods = @{$info->{requires}}; |
|
0
|
|
|
|
|
0
|
|
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
0
|
0
|
|
|
|
0
|
return wantarray ? @methods : \@methods; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub _derive_role_type { |
94
|
5
|
|
|
5
|
|
18
|
my $role = shift; |
95
|
|
|
|
|
|
|
|
96
|
5
|
|
|
|
|
31
|
try_load_class($role); |
97
|
|
|
|
|
|
|
|
98
|
5
|
0
|
33
|
|
|
1620
|
if ($role->can('meta') && $role->meta()->isa('Moose::Meta::Role')) { |
99
|
|
|
|
|
|
|
# Also covers newer Moo::Roles |
100
|
0
|
|
|
|
|
|
return 'Moose::Role'; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
0
|
0
|
0
|
|
|
|
if (try_load_class('Role::Tiny') && _role_tiny_info($role)) { |
104
|
|
|
|
|
|
|
# Also covers older Moo::Roles |
105
|
0
|
|
|
|
|
|
return 'Role::Tiny'; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
0
|
|
|
|
|
|
return; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
my $package_counter = 0; |
112
|
|
|
|
|
|
|
sub _package_name { |
113
|
0
|
|
|
0
|
|
|
return 'MooseX::Test::Role::Consumer' . $package_counter++; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub _apply_role { |
117
|
0
|
|
|
0
|
|
|
my %args = @_; |
118
|
|
|
|
|
|
|
|
119
|
0
|
|
|
|
|
|
my $package = $args{package}; |
120
|
0
|
|
|
|
|
|
my $role_type = $args{role_type}; |
121
|
0
|
|
|
|
|
|
my $role = $args{role}; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# We'll need a thing that exports a "with" sub |
124
|
0
|
|
|
|
|
|
my $with_exporter; |
125
|
0
|
0
|
|
|
|
|
if ($role_type eq 'Moose::Role') { |
|
|
0
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
$with_exporter = 'Moose'; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
elsif ($role_type eq 'Role::Tiny') { |
129
|
0
|
|
|
|
|
|
$with_exporter = 'Role::Tiny::With'; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
else { |
132
|
0
|
|
|
|
|
|
confess "Unknown role type $role_type"; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
0
|
|
|
|
|
|
my $source = qq{ |
136
|
|
|
|
|
|
|
package $package; |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
use $with_exporter; |
139
|
|
|
|
|
|
|
with('$role'); |
140
|
|
|
|
|
|
|
}; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
#warn $source; |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
|
eval($source); |
145
|
0
|
0
|
|
|
|
|
die $@ if $@; |
146
|
|
|
|
|
|
|
|
147
|
0
|
|
|
|
|
|
return $package; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub _add_methods { |
151
|
0
|
|
|
0
|
|
|
my %args = @_; |
152
|
|
|
|
|
|
|
|
153
|
0
|
|
|
|
|
|
my $role_type = $args{role_type}; |
154
|
0
|
|
|
|
|
|
my $package = $args{package}; |
155
|
0
|
|
|
|
|
|
my $role = $args{role}; |
156
|
0
|
|
|
|
|
|
my $methods = $args{methods}; |
157
|
|
|
|
|
|
|
|
158
|
0
|
|
0
|
0
|
|
|
$methods->{$_} ||= sub { undef } for _required_methods( $role_type, $role ); |
|
0
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
|
my $meta; |
161
|
0
|
0
|
|
|
|
|
$meta = Moose::Meta::Class->create($package) if $role_type eq 'Moose::Role'; |
162
|
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
|
while ( my ( $method, $subref ) = each(%{$methods}) ) { |
|
0
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# This allows us to have scalar values without an anonymous sub in the |
165
|
|
|
|
|
|
|
# definition, similar to Moose's default values. We need to make a lexical |
166
|
|
|
|
|
|
|
# copy of $subref so we do not build a circle where we return itself as a |
167
|
|
|
|
|
|
|
# coderef later on. |
168
|
0
|
0
|
|
|
|
|
if ( !ref $subref ) { |
169
|
0
|
|
|
|
|
|
my $value = $subref; |
170
|
0
|
|
|
0
|
|
|
$subref = sub { $value }; |
|
0
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
0
|
0
|
|
|
|
|
if ($meta) { |
174
|
0
|
|
|
|
|
|
$meta->add_method($method => $subref); |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
else { |
177
|
7
|
|
|
7
|
|
69
|
no strict 'refs'; |
|
7
|
|
|
|
|
27
|
|
|
7
|
|
|
|
|
1838
|
|
178
|
|
|
|
|
|
|
#no warnings 'redefine'; |
179
|
0
|
|
|
|
|
|
*{ $package . '::' . $method } = $subref; |
|
0
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
0
|
|
|
|
|
|
return; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub _role_tiny_info { |
187
|
|
|
|
|
|
|
# This seems brittle, but there aren't many options to get this data. |
188
|
|
|
|
|
|
|
# Moo relies on %INFO too, so it seems like it would be a hard thing |
189
|
|
|
|
|
|
|
# for to move away from. |
190
|
|
|
|
|
|
|
|
191
|
0
|
|
|
0
|
|
|
my $role = shift; |
192
|
0
|
|
|
|
|
|
return $Role::Tiny::INFO{$role}; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
my $Test = Test::Builder->new(); |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# Done this way for easier testing |
198
|
|
|
|
|
|
|
our $ok = sub { $Test->ok(@_) }; |
199
|
0
|
|
|
0
|
0
|
|
sub ok { $ok->(@_) } |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
1; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=pod |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head1 NAME |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
MooseX::Test::Role - Test functions for Moose roles |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head1 SYNOPSIS |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
use MooseX::Test::Role; |
212
|
|
|
|
|
|
|
use Test::More tests => 3; |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
requires_ok('MyRole', qw/method1 method2/); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my $consumer = consuming_object( |
217
|
|
|
|
|
|
|
'MyRole', |
218
|
|
|
|
|
|
|
methods => { |
219
|
|
|
|
|
|
|
method1 => 1, |
220
|
|
|
|
|
|
|
method2 => sub { shift->method1() }, |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
); |
223
|
|
|
|
|
|
|
ok( $consumer->myrole_method ); |
224
|
|
|
|
|
|
|
is( $consumer->method1, 1 ); |
225
|
|
|
|
|
|
|
is( $consumer->method2, 1 ); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
my $consuming_class = consuming_class('MyRole'); |
228
|
|
|
|
|
|
|
ok( $consuming_class->class_method() ); |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head1 DESCRIPTION |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Provides functions for testing roles. Supports roles created with |
233
|
|
|
|
|
|
|
L, L or L. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=head1 BACKGROUND |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
Unit testing a role can be hard. A major problem is creating classes that |
238
|
|
|
|
|
|
|
consume the role. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
One could side-step the problem entirely and just call the subroutines in the |
241
|
|
|
|
|
|
|
role's package directly. For example, |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Fooable->bar(); |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
That only works until C calls another method in the consuming class |
246
|
|
|
|
|
|
|
though. Mock objects are a tempting way to solve that problem: |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
my $consumer = Test::MockObject->new(); |
249
|
|
|
|
|
|
|
$consumer->set_always('baz', 1); |
250
|
|
|
|
|
|
|
Fooable::bar($consumer); |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
But if C happens to call another method in the role then |
253
|
|
|
|
|
|
|
the mock consumer will have to mock that method too. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
A better way is to create a class to consume the role: |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
package FooableTest; |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
use Moose; |
260
|
|
|
|
|
|
|
with 'Fooable'; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
sub required_method {} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
package main; |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
my $consumer = FooableTest->new(); |
267
|
|
|
|
|
|
|
$consumer->bar(); |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
This can work well for some roles. Unfortunately, if several variations have to |
270
|
|
|
|
|
|
|
be tested, it may be necessary to create several consuming test classes, which |
271
|
|
|
|
|
|
|
gets tedious. |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Moose can create anonymous classes which consume roles: |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
my $consumer = Moose::Meta::Class->create_anon_class( |
276
|
|
|
|
|
|
|
roles => ['Fooable'], |
277
|
|
|
|
|
|
|
methods => { |
278
|
|
|
|
|
|
|
required_method => sub {}, |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
)->new_object(); |
281
|
|
|
|
|
|
|
$consumer->bar(); |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
This can still be tedious, especially for roles that require lots of methods. |
284
|
|
|
|
|
|
|
C simply makes this easier to do. |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=head1 EXPORTED FUNCTIONS |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=over 4 |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=item C \%methods)> |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Creates a class which consumes the role, and returns it's package name. |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
C<$role> must be the package name of a role. L, L and |
295
|
|
|
|
|
|
|
L are supported. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
Any method required by the role will be stubbed. To override the default stub |
298
|
|
|
|
|
|
|
methods, or to add additional methods, specify the name and a coderef: |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
consuming_class('MyRole', |
301
|
|
|
|
|
|
|
method1 => sub { 'one' }, |
302
|
|
|
|
|
|
|
method2 => sub { 'two' }, |
303
|
|
|
|
|
|
|
required_method => sub { 'required' }, |
304
|
|
|
|
|
|
|
); |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
For methods that should just return a fixed scalar value, you can ommit the |
307
|
|
|
|
|
|
|
coderef. |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
consuming_class('MyRole', |
310
|
|
|
|
|
|
|
method1 => 'one', |
311
|
|
|
|
|
|
|
method_uc1 => sub { |
312
|
|
|
|
|
|
|
my ($self) = @_; |
313
|
|
|
|
|
|
|
lc $self->one; |
314
|
|
|
|
|
|
|
}, |
315
|
|
|
|
|
|
|
); |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=item C \%methods)> |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Creates a class which consumes the role, and returns an instance of it. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
If the class does not have a C method (which is commonly the case for |
322
|
|
|
|
|
|
|
L), then the package name will be returned instead. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
See C for arguments. C is essentially |
325
|
|
|
|
|
|
|
equivalent to: |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
consuming_class(@_)->new(); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item C |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Alias of C, without named arguments. This is left in for |
332
|
|
|
|
|
|
|
compatibility, new code should use C. |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
=item C |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
Tests if role requires one or more methods. |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=back |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=head1 GITHUB |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
Patches, comments or mean-spirited code reviews are all welcomed on GitHub: |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
L |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=head1 AUTHOR |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
Paul Boyd |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
This software is copyright (c) 2014 by Paul Boyd. |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
355
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=cut |