File Coverage

blib/lib/Test/Mockify/Method.pm
Criterion Covered Total %
statement 110 111 99.1
branch 41 42 97.6
condition 15 15 100.0
subroutine 17 17 100.0
pod 3 4 75.0
total 186 189 98.4


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 is used to provide the chained mock setup
10              
11             =head1 METHODS
12              
13             =cut
14             package Test::Mockify::Method;
15 18     18   74130 use Test::Mockify::Parameter;
  18         67  
  18         594  
16 18     18   114 use Data::Dumper;
  18         38  
  18         937  
17 18         1088 use Test::Mockify::TypeTests qw (
18             IsInteger
19             IsFloat
20             IsString
21             IsArrayReference
22             IsHashReference
23             IsObjectReference
24             IsCodeReference
25 18     18   108 );
  18         38  
26 18     18   8143 use Test::Mockify::Matcher qw (SupportedTypes);
  18         46  
  18         1685  
27 18     18   116 use Scalar::Util qw( blessed );
  18         39  
  18         706  
28 18     18   98 use strict;
  18         70  
  18         416  
29 18     18   93 use Test::Mockify::Tools qw (Error);
  18         31  
  18         613  
30 18     18   139 use warnings;
  18         38  
  18         18516  
31              
32             #---------------------------------------------------------------------
33             sub new {
34 144     144 0 6276 my $Class = shift;
35 144         474 my $self = bless {
36             'TypeStore'=> undef,
37             'MatcherStore'=> undef,
38             'AnyStore'=> undef,
39             }, $Class;
40 144         433 foreach my $Type (SupportedTypes()){
41 144         631 $self->{'MatcherStore'}{$Type} = [];
42             }
43 144         514 return $self;
44             }
45             =pod
46              
47             =head2 when
48              
49             C have to be called with a L to specify the expected parameter list (signature).
50             This will create for every signature a L Object which will stored and also returned. So it is possible to create multiple signatures for one Method.
51             It is not possible to mix C with C.
52              
53             when(String())
54             when(Number(),String('abc'))
55              
56             =cut
57             sub when {
58 151     151 1 312 my $self = shift;
59 151         305 my @Parameters = @_;
60 151         191 my @Signature;
61 151         196 foreach my $Signature (keys %{$self->{'TypeStore'}}){
  151         488  
62 67 100       141 if($Signature eq 'UsedWithWhenAny'){
63 1         5 Error('It is not possible to mix "when" and "whenAny" for the same method.');
64             }
65             }
66 150         281 foreach my $hParameter ( @Parameters ){
67 160 100       347 Error('Use Test::Mockify::Matcher to define proper matchers.') unless (ref($hParameter) eq 'HASH');
68 159         314 push(@Signature, $hParameter->{'Type'});
69             }
70 149         462 $self->_checkExpectedParameters(\@Parameters);
71 142         1738 return $self->_addToTypeStore(\@Signature, \@Parameters);
72             }
73             =pod
74              
75             =head2 whenAny
76              
77             C have to be called without parameter, when called it will accept any type and amount of parameter. It will return a L Object.
78             It is not possible to mix C with C.
79              
80             whenAny()
81              
82             =cut
83             sub whenAny {
84 12     12 1 123 my $self = shift;
85 12 100       70 Error ('"whenAny" doesn\'t allow any parameters' ) if (@_);
86 11 100       17 if((scalar keys %{$self->{'TypeStore'}})){
  11         49  
87 2         6 Error('You can use "whenAny" only once. Additionaly, it is not possible to mix "when" and "whenAny" for the same method.');
88             }
89 9         41 return $self->_addToTypeStore(['UsedWithWhenAny']);
90             }
91              
92             #---------------------------------------------------------------------
93             sub _checkExpectedParameters{
94 149     149   198 my $self = shift;
95 149         229 my ( $NewExpectedParameters) = @_;
96 149         214 my $SignatureKey = '';
97 149         223 for(my $i = 0; $i < scalar @{$NewExpectedParameters}; $i++){ ## no critic (ProhibitCStyleForLoops) i need the counter
  304         613  
98 159         227 my $Type = $NewExpectedParameters->[$i]->{'Type'};
99 159         236 $SignatureKey .= $Type;
100 159         218 my $NewExpectedParameter = $NewExpectedParameters->[$i];
101 159         569 $self->_testMatcherStore($self->{'MatcherStore'}{$Type}->[$i], $NewExpectedParameter);
102 157         334 $self->{'MatcherStore'}{$Type}->[$i] = $NewExpectedParameter;
103 157         438 $self->_testAnyStore($self->{'AnyStore'}->[$i], $Type);
104 155         302 $self->{'AnyStore'}->[$i] = $Type;
105             }
106              
107 145         190 foreach my $ExistingParameter (@{$self->{'TypeStore'}{$SignatureKey}}){
  145         454  
108 9 100       49 if($ExistingParameter->compareExpectedParameters($NewExpectedParameters)){
109 3         661 Error('You can use a method signature only once.');
110             }
111             }
112             }
113              
114             #---------------------------------------------------------------------
115             sub _testMatcherStore {
116 159     159   213 my $self = shift;
117 159         327 my ($MatcherStore, $NewExpectedParameterValue) = @_;
118 159 100       304 if( defined $NewExpectedParameterValue->{'Value'} ){
119 88 100 100     247 if($MatcherStore and not $MatcherStore->{'Value'}){
120 1         6 Error('It is not possibel to mix "expected parameter" with previously set "any parameter".');
121             }
122             } else {
123 71 100 100     128 if($MatcherStore && $MatcherStore->{'Value'}){
124 1         3 Error('It is not possibel to mix "any parameter" with previously set "expected parameter".');
125             }
126             }
127 157         221 return;
128             }
129             #---------------------------------------------------------------------
130             sub _testAnyStore {
131 157     157   209 my $self = shift;
132 157         271 my ($AnyStore, $Type) = @_;
133 157 100       275 if($AnyStore){
134 29 100 100     77 if($AnyStore eq 'any' and $Type ne 'any'){
135 1         4 Error('It is not possibel to mix "specific type" with previously set "any type".');
136             }
137 28 100 100     89 if($AnyStore ne 'any' and $Type eq 'any'){
138 1         3 Error('It is not possibel to mix "any type" with previously set "specific type".');
139             }
140             }
141 155         210 return;
142             }
143             #---------------------------------------------------------------------
144             sub _addToTypeStore {
145 124     124   207 my $self = shift;
146 124         208 my ($Signature, $NewExpectedParameters) = @_;
147 124         170 my $SignatureKey = join('',@{$Signature});
  124         228  
148 124         504 my $Parameter = Test::Mockify::Parameter->new($NewExpectedParameters);
149 124         160 push(@{$self->{'TypeStore'}{$SignatureKey}}, $Parameter );
  124         262  
150 124         306 return $Parameter->buildReturn();
151             }
152             =pod
153              
154             =head2 call
155              
156             C 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.
157              
158             call()
159              
160             =cut
161             sub call {
162 175     175 1 327 my $self = shift;
163 175         319 my @Parameters = @_;
164 175         297 my $SignatureKey = '';
165 175         442 for(my $i = 0; $i < scalar @Parameters; $i++){ ## no critic (ProhibitCStyleForLoops) i need the counter
166 200 100 100     701 if($self->{'AnyStore'}->[$i] && $self->{'AnyStore'}->[$i] eq 'any'){
167 18         45 $SignatureKey .= 'any';
168             }else{
169 182         429 $SignatureKey .= $self->_getType($Parameters[$i]);
170             }
171             }
172 175 100       382 if($self->{'TypeStore'}{'UsedWithWhenAny'}){
173 13         42 return $self->{'TypeStore'}{'UsedWithWhenAny'}->[0]->call(@Parameters);
174             }else {
175 162         243 foreach my $ExistingParameter (@{$self->{'TypeStore'}{$SignatureKey}}){
  162         415  
176 156 100       453 if($ExistingParameter->matchWithExpectedParameters(@Parameters)){
177 142         343 return $ExistingParameter->call(@Parameters);
178             }
179             }
180             }
181 20         100 Error ("No matching found for signatur type '$SignatureKey' \nvalues:".Dumper(\@Parameters));
182             }
183             #---------------------------------------------------------------------
184             sub _getType{
185 182     182   301 my $self = shift;
186 182         310 my ($Parameter) = @_;
187 182 100       417 return 'arrayref' if(IsArrayReference($Parameter));
188 172 100       427 return 'hashref' if(IsHashReference($Parameter));
189 162 100       335 return 'object' if(IsObjectReference($Parameter));
190 148 100       289 return 'sub' if(IsCodeReference($Parameter));
191 144 100       276 return 'number' if(IsFloat($Parameter));
192 75 100       162 return 'string' if(IsString($Parameter));
193 8 50       36 return 'undef' if( not $Parameter);
194 0           Error("UnexpectedParameterType for: '$Parameter'");
195             }
196              
197             1;
198              
199             __END__