File Coverage

blib/lib/Test/Mockify/Method.pm
Criterion Covered Total %
statement 105 106 99.0
branch 41 42 97.6
condition 14 15 93.3
subroutine 16 16 100.0
pod 3 4 75.0
total 179 183 97.8


line stmt bran cond sub pod time code
1             =pod
2            
3             =head1 Name
4            
5             Test::Mockify::Method - chained setup
6            
7             =head1 DESCRIPTION
8            
9             L<Test::Mockify::Method> is used to provide the chained mock setup
10            
11             =head1 METHODS
12            
13             =cut
14             package Test::Mockify::Method;
15 6     6   28061 use Test::Mockify::Parameter;
  6         16  
  6         177  
16 6     6   1210 use Data::Dumper;
  6         16429  
  6         361  
17 6         360 use Test::Mockify::TypeTests qw (
18             IsInteger
19             IsFloat
20             IsString
21             IsArrayReference
22             IsHashReference
23             IsObjectReference
24             IsCodeReference
25 6     6   32 );
  6         10  
26 6     6   1956 use Test::Mockify::Matcher qw (SupportedTypes);
  6         8  
  6         298  
27 6     6   26 use Scalar::Util qw( blessed );
  6         6  
  6         200  
28 6     6   23 use strict;
  6         8  
  6         106  
29 6     6   23 use warnings;
  6         5  
  6         4586  
30              
31             #---------------------------------------------------------------------
32             sub new {
33 86     86 0 4032     my $Class = shift;
34 86         231     my $self = bless {
35                     'TypeStore'=> undef,
36                     'MatcherStore'=> undef,
37                     'AnyStore'=> undef,
38                 }, $Class;
39 86         194     foreach my $Type (SupportedTypes()){
40 86         271         $self->{'MatcherStore'}{$Type} = [];
41                 }
42 86         209     return $self;
43             }
44             =pod
45            
46             =head2 when
47            
48             C<when> have to be called with a L<Test::Mockify::Matcher> to specify the expected parameter list (signature).
49             This will create for every signature a Parameter Object which will stored and also returned. So it is possible to create multiple signatures for one Method.
50             It is not possible to mix C<when> with C<whenAny>.
51            
52             when(String())
53             when(Number(),String('abc'))
54            
55             =cut
56             sub when {
57 85     85 1 100     my $self = shift;
58 85         117     my @Parameters = @_;
59 85         70     my @Signature;
60 85         66     foreach my $Signature (keys %{$self->{'TypeStore'}}){
  85         196  
61 52 100       79         if($Signature eq 'UsedWithWhenAny'){
62 1         11             die('It is not possible to use a mixture between "when" and "whenAny"');
63                     }
64                 }
65 84         104     foreach my $hParameter ( @Parameters ){
66 119 100       229         die('Use Test::Mockify::Matcher to define proper matchers.') unless (ref($hParameter) eq 'HASH');
67 118         155         push(@Signature, $hParameter->{'Type'});
68                 }
69 83         167     $self->_checkExpectedParameters(\@Parameters);
70 76         457     return $self->_addToTypeStore(\@Signature, \@Parameters);
71             }
72             =pod
73            
74             =head2 whenAny
75            
76             C<whenAny> have to be called without parameter, when called it will accept any type and amount of parameter. It will return a Parameter Object.
77             It is not possible to mix C<whenAny> with C<when>.
78            
79             whenAny()
80            
81             =cut
82             sub whenAny {
83 17     17 1 96     my $self = shift;
84 17 100       57     die ('"whenAny" don`t allow any parameters' ) if (@_);
85 16 100       69     if((scalar keys %{$self->{'TypeStore'}})){
  16         57  
86 2         18         die('"whenAny" can only used once. Also it is not possible to use a mixture between "when" and "whenAny"');
87                 }
88 14         42     return $self->_addToTypeStore(['UsedWithWhenAny']);
89             }
90             #---------------------------------------------------------------------
91             sub _checkExpectedParameters{
92 83     83   65     my $self = shift;
93 83         77     my ( $NewExpectedParameters) = @_;
94 83         74     my $SignatureKey = '';
95 83         170     for(my $i = 0; $i < scalar @$NewExpectedParameters; $i++){
96 118         177         my $Type = $NewExpectedParameters->[$i]->{'Type'};
97 118         113         $SignatureKey .= $Type;
98 118         96         my $NewExpectedParameter = $NewExpectedParameters->[$i];
99 118         333         $self->_testMatcherStore($self->{'MatcherStore'}{$Type}->[$i], $NewExpectedParameter);
100 116         187         $self->{'MatcherStore'}{$Type}->[$i] = $NewExpectedParameter;
101 116         209         $self->_testAnyStore($self->{'AnyStore'}->[$i], $Type);
102 114         253         $self->{'AnyStore'}->[$i] = $Type;
103                 }
104              
105 79         59     foreach my $ExistingParameter (@{$self->{'TypeStore'}{$SignatureKey}}){
  79         195  
106 5 100       14         if($ExistingParameter->compareExpectedParameters($NewExpectedParameters)){
107 3         413             die('It is not possible two add two times the same method Signature.');
108                     }
109                 }
110             }
111              
112             #---------------------------------------------------------------------
113             sub _testMatcherStore {
114 118     118   83     my $self = shift;
115 118         155     my ($MatcherStore, $NewExpectedParameterValue) = @_;
116 118 100       174     if( $NewExpectedParameterValue->{'Value'} ){
117 49 100 100     125         if($MatcherStore and not $MatcherStore->{'Value'}){
118 1         8             die('It is not possibel to mix "expected parameter" with previously set "any parameter".');
119                     }
120                 } else {
121 69 100 66     109         if($MatcherStore && $MatcherStore->{'Value'}){
122 1         8             die('It is not possibel to mix "any parameter" with previously set "expected parameter".');
123                     }
124                 }
125 116         111     return;
126             }
127             #---------------------------------------------------------------------
128             sub _testAnyStore {
129 116     116   90     my $self = shift;
130 116         118     my ($AnyStore, $Type) = @_;
131 116 100       171     if($AnyStore){
132 22 100 100     52         if($AnyStore eq 'any' and $Type ne 'any'){
133 1         9             die('It is not possibel to mix "specific type" with previously set "any type".');
134                     }
135 21 100 100     72         if($AnyStore ne 'any' and $Type eq 'any'){
136 1         8             die('It is not possibel to mix "any type" with previously set "specific type".');
137                     }
138                 }
139 114         104     return;
140             }
141             #---------------------------------------------------------------------
142             sub _addToTypeStore {
143 73     73   66     my $self = shift;
144 73         61     my ($Signature, $NewExpectedParameters) = @_;
145 73         111     my $SignatureKey = join('',@$Signature);
146 73         191     my $Parameter = Test::Mockify::Parameter->new($NewExpectedParameters);
147 73         56     push(@{$self->{'TypeStore'}{$SignatureKey}}, $Parameter );
  73         113  
148 73         136     return $Parameter->buildReturn();
149             }
150             =pod
151            
152             =head2 call
153            
154             C<call> will be called with a list of parameters. If the signature of this parameters match a stored signature it will call the corresponding parameter object.
155            
156             call()
157            
158             =cut
159             sub call {
160 102     102 1 122     my $self = shift;
161 102         122     my @Parameters = @_;
162 102         90     my $SignatureKey = '';
163 102         234     for(my $i = 0; $i < scalar @Parameters; $i++){
164 136 100 100     465         if($self->{'AnyStore'}->[$i] && $self->{'AnyStore'}->[$i] eq 'any'){
165 9         24             $SignatureKey .= 'any';
166                     }else{
167 127         212             $SignatureKey .= $self->_getType($Parameters[$i]);
168                     }
169                 }
170 102 100       157     if($self->{'TypeStore'}{'UsedWithWhenAny'}){
171 17         113         return $self->{'TypeStore'}{'UsedWithWhenAny'}->[0]->call(@Parameters);
172                 }else {
173 85         90         foreach my $ExistingParameter (@{$self->{'TypeStore'}{$SignatureKey}}){
  85         149  
174 76 100       181             if($ExistingParameter->matchWithExpectedParameters(@Parameters)){
175 67         181                 return $ExistingParameter->call(@Parameters);
176                         }
177                     }
178                 }
179 18         67     die ("No matching found for $SignatureKey -> ".Dumper(\@Parameters));
180             }
181             #---------------------------------------------------------------------
182             sub _getType{
183 127     127   90     my $self = shift;
184 127         114     my ($Parameter) = @_;
185 127 100       234     return 'arrayref' if(IsArrayReference($Parameter));
186 115 100       261     return 'hashref' if(IsHashReference($Parameter));
187 103 100       178     return 'object' if(IsObjectReference($Parameter));
188 90 100       144     return 'sub' if(IsCodeReference($Parameter));
189 86 100       159     return 'number' if(IsFloat($Parameter));
190 68 100       130     return 'string' if(IsString($Parameter));
191 9 50       41     return 'undef' if( not $Parameter);
192 0               die("UnexpectedParameterType for: '$Parameter'");
193             }
194              
195             1;
196              
197             __END__
198            
199             =head1 LICENSE
200            
201             Copyright (C) 2017 ePages GmbH
202            
203             This library is free software; you can redistribute it and/or modify
204             it under the same terms as Perl itself.
205            
206             =head1 AUTHOR
207            
208             Christian Breitkreutz E<lt>christianbreitkreutz@gmx.deE<gt>
209            
210             =cut
211