Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
.. index::
2
    single: Argument Validation
3
 
4
Argument Validation
5
===================
6
 
7
The arguments passed to the ``with()`` declaration when setting up an
8
expectation determine the criteria for matching method calls to expectations.
9
Thus, we can setup up many expectations for a single method, each
10
differentiated by the expected arguments. Such argument matching is done on a
11
"best fit" basis.  This ensures explicit matches take precedence over
12
generalised matches.
13
 
14
An explicit match is merely where the expected argument and the actual
15
argument are easily equated (i.e. using ``===`` or ``==``). More generalised
16
matches are possible using regular expressions, class hinting and the
17
available generic matchers. The purpose of generalised matchers is to allow
18
arguments be defined in non-explicit terms, e.g. ``Mockery::any()`` passed to
19
``with()`` will match **any** argument in that position.
20
 
21
Mockery's generic matchers do not cover all possibilities but offers optional
22
support for the Hamcrest library of matchers. Hamcrest is a PHP port of the
23
similarly named Java library (which has been ported also to Python, Erlang,
24
etc). By using Hamcrest, Mockery does not need to duplicate Hamcrest's already
25
impressive utility which itself promotes a natural English DSL.
26
 
27
The examples below show Mockery matchers and their Hamcrest equivalent, if there
28
is one. Hamcrest uses functions (no namespacing).
29
 
30
.. note::
31
 
32
    If you don't wish to use the global Hamcrest functions, they are all exposed
33
    through the ``\Hamcrest\Matchers`` class as well, as static methods. Thus,
34
    ``identicalTo($arg)`` is the same as ``\Hamcrest\Matchers::identicalTo($arg)``
35
 
36
The most common matcher is the ``with()`` matcher:
37
 
38
.. code-block:: php
39
 
40
    $mock = \Mockery::mock('MyClass');
41
    $mock->shouldReceive('foo')
42
        ->with(1):
43
 
44
It tells mockery that it should receive a call to the ``foo`` method with the
45
integer ``1`` as an argument. In cases like this, Mockery first tries to match
46
the arguments using ``===`` (identical) comparison operator. If the argument is
47
a primitive, and if it fails the identical comparison, Mockery does a fallback
48
to the ``==`` (equals) comparison operator.
49
 
50
When matching objects as arguments, Mockery only does the strict ``===``
51
comparison, which means only the same ``$object`` will match:
52
 
53
.. code-block:: php
54
 
55
    $object = new stdClass();
56
    $mock = \Mockery::mock('MyClass');
57
    $mock->shouldReceive("foo")
58
        ->with($object);
59
 
60
    // Hamcrest equivalent
61
    $mock->shouldReceive("foo")
62
        ->with(identicalTo($object));
63
 
64
A different instance of ``stdClass`` will **not** match.
65
 
66
.. note::
67
 
68
    The ``Mockery\Matcher\MustBe`` matcher has been deprecated.
69
 
70
If we need a loose comparison of objects, we can do that using Hamcrest's
71
``equalTo`` matcher:
72
 
73
.. code-block:: php
74
 
75
    $mock->shouldReceive("foo")
76
        ->with(equalTo(new stdClass));
77
 
78
In cases when we don't care about the type, or the value of an argument, just
79
that any argument is present, we use ``any()``:
80
 
81
.. code-block:: php
82
 
83
    $mock = \Mockery::mock('MyClass');
84
    $mock->shouldReceive("foo")
85
        ->with(\Mockery::any());
86
 
87
    // Hamcrest equivalent
88
    $mock->shouldReceive("foo")
89
        ->with(anything())
90
 
91
Anything and everything passed in this argument slot is passed unconstrained.
92
 
93
Validating Types and Resources
94
------------------------------
95
 
96
The ``type()`` matcher accepts any string which can be attached to ``is_`` to
97
form a valid type check.
98
 
99
To match any PHP resource, we could do the following:
100
 
101
.. code-block:: php
102
 
103
    $mock = \Mockery::mock('MyClass');
104
    $mock->shouldReceive("foo")
105
        ->with(\Mockery::type('resource'));
106
 
107
    // Hamcrest equivalents
108
    $mock->shouldReceive("foo")
109
        ->with(resourceValue());
110
    $mock->shouldReceive("foo")
111
        ->with(typeOf('resource'));
112
 
113
It will return a ``true`` from an ``is_resource()`` call, if the provided
114
argument to the method is a PHP resource. For example, ``\Mockery::type('float')``
115
or Hamcrest's ``floatValue()`` and ``typeOf('float')`` checks use ``is_float()``,
116
and ``\Mockery::type('callable')`` or Hamcrest's ``callable()`` uses
117
``is_callable()``.
118
 
119
The ``type()`` matcher also accepts a class or interface name to be used in an
120
``instanceof`` evaluation of the actual argument. Hamcrest uses ``anInstanceOf()``.
121
 
122
A full list of the type checkers is available at
123
`php.net <http://www.php.net/manual/en/ref.var.php>`_ or browse Hamcrest's function
124
list in
125
`the Hamcrest code <https://github.com/hamcrest/hamcrest-php/blob/master/hamcrest/Hamcrest.php>`_.
126
 
127
.. _argument-validation-complex-argument-validation:
128
 
129
Complex Argument Validation
130
---------------------------
131
 
132
If we want to perform a complex argument validation, the ``on()`` matcher is
133
invaluable. It accepts a closure (anonymous function) to which the actual
134
argument will be passed.
135
 
136
.. code-block:: php
137
 
138
    $mock = \Mockery::mock('MyClass');
139
    $mock->shouldReceive("foo")
140
        ->with(\Mockery::on(closure));
141
 
142
If the closure evaluates to (i.e. returns) boolean ``true`` then the argument is
143
assumed to have matched the expectation.
144
 
145
.. code-block:: php
146
 
147
    $mock = \Mockery::mock('MyClass');
148
 
149
    $mock->shouldReceive('foo')
150
        ->with(\Mockery::on(function ($argument) {
151
            if ($argument % 2 == 0) {
152
                return true;
153
            }
154
            return false;
155
        }));
156
 
157
    $mock->foo(4); // matches the expectation
158
    $mock->foo(3); // throws a NoMatchingExpectationException
159
 
160
.. note::
161
 
162
    There is no Hamcrest version of the ``on()`` matcher.
163
 
164
We can also perform argument validation by passing a closure to ``withArgs()``
165
method. The closure will receive all arguments passed in the call to the expected
166
method and if it evaluates (i.e. returns) to boolean ``true``, then the list of
167
arguments is assumed to have matched the expectation:
168
 
169
.. code-block:: php
170
 
171
    $mock = \Mockery::mock('MyClass');
172
    $mock->shouldReceive("foo")
173
        ->withArgs(closure);
174
 
175
The closure can also handle optional parameters, so if an optional parameter is
176
missing in the call to the expected method, it doesn't necessary means that the
177
list of arguments doesn't match the expectation.
178
 
179
.. code-block:: php
180
 
181
    $closure = function ($odd, $even, $sum = null) {
182
        $result = ($odd % 2 != 0) && ($even % 2 == 0);
183
        if (!is_null($sum)) {
184
            return $result && ($odd + $even == $sum);
185
        }
186
        return $result;
187
    };
188
 
189
    $mock = \Mockery::mock('MyClass');
190
    $mock->shouldReceive('foo')->withArgs($closure);
191
 
192
    $mock->foo(1, 2); // It matches the expectation: the optional argument is not needed
193
    $mock->foo(1, 2, 3); // It also matches the expectation: the optional argument pass the validation
194
    $mock->foo(1, 2, 4); // It doesn't match the expectation: the optional doesn't pass the validation
195
 
196
.. note::
197
 
198
    In previous versions, Mockery's ``with()`` would attempt to do a pattern
199
    matching against the arguments, attempting to use the argument as a
200
    regular expression. Over time this proved to be not such a great idea, so
201
    we removed this functionality, and have introduced ``Mockery::pattern()``
202
    instead.
203
 
204
If we would like to match an argument against a regular expression, we can use
205
the ``\Mockery::pattern()``:
206
 
207
.. code-block:: php
208
 
209
    $mock = \Mockery::mock('MyClass');
210
    $mock->shouldReceive('foo')
211
        ->with(\Mockery::pattern('/^foo/'));
212
 
213
    // Hamcrest equivalent
214
    $mock->shouldReceive('foo')
215
        ->with(matchesPattern('/^foo/'));
216
 
217
The ``ducktype()`` matcher is an alternative to matching by class type:
218
 
219
.. code-block:: php
220
 
221
    $mock = \Mockery::mock('MyClass');
222
    $mock->shouldReceive('foo')
223
        ->with(\Mockery::ducktype('foo', 'bar'));
224
 
225
It matches any argument which is an object containing the provided list of
226
methods to call.
227
 
228
.. note::
229
 
230
    There is no Hamcrest version of the ``ducktype()`` matcher.
231
 
232
Capturing Arguments
233
-------------------
234
 
235
If we want to perform multiple validations on a single argument, the ``capture``
236
matcher provides a streamlined alternative to using the ``on()`` matcher.
237
It accepts a variable which the actual argument will be assigned.
238
 
239
.. code-block:: php
240
 
241
    $mock = \Mockery::mock('MyClass');
242
    $mock->shouldReceive("foo")
243
        ->with(\Mockery::capture($bar));
244
 
245
This will assign *any* argument passed to ``foo`` to the local ``$bar`` variable to
246
then perform additional validation using assertions.
247
 
248
.. note::
249
 
250
    The ``capture`` matcher always evaluates to ``true``. As such, we should always
251
    perform additional argument validation.
252
 
253
Additional Argument Matchers
254
----------------------------
255
 
256
The ``not()`` matcher matches any argument which is not equal or identical to
257
the matcher's parameter:
258
 
259
.. code-block:: php
260
 
261
    $mock = \Mockery::mock('MyClass');
262
    $mock->shouldReceive('foo')
263
        ->with(\Mockery::not(2));
264
 
265
    // Hamcrest equivalent
266
    $mock->shouldReceive('foo')
267
        ->with(not(2));
268
 
269
``anyOf()`` matches any argument which equals any one of the given parameters:
270
 
271
.. code-block:: php
272
 
273
    $mock = \Mockery::mock('MyClass');
274
    $mock->shouldReceive('foo')
275
        ->with(\Mockery::anyOf(1, 2));
276
 
277
    // Hamcrest equivalent
278
    $mock->shouldReceive('foo')
279
        ->with(anyOf(1,2));
280
 
281
``notAnyOf()`` matches any argument which is not equal or identical to any of
282
the given parameters:
283
 
284
.. code-block:: php
285
 
286
    $mock = \Mockery::mock('MyClass');
287
    $mock->shouldReceive('foo')
288
        ->with(\Mockery::notAnyOf(1, 2));
289
 
290
.. note::
291
 
292
    There is no Hamcrest version of the ``notAnyOf()`` matcher.
293
 
294
``subset()`` matches any argument which is any array containing the given array
295
subset:
296
 
297
.. code-block:: php
298
 
299
    $mock = \Mockery::mock('MyClass');
300
    $mock->shouldReceive('foo')
301
        ->with(\Mockery::subset(array(0 => 'foo')));
302
 
303
This enforces both key naming and values, i.e. both the key and value of each
304
actual element is compared.
305
 
306
.. note::
307
 
308
    There is no Hamcrest version of this functionality, though Hamcrest can check
309
    a single entry using ``hasEntry()`` or ``hasKeyValuePair()``.
310
 
311
``contains()`` matches any argument which is an array containing the listed
312
values:
313
 
314
.. code-block:: php
315
 
316
    $mock = \Mockery::mock('MyClass');
317
    $mock->shouldReceive('foo')
318
        ->with(\Mockery::contains(value1, value2));
319
 
320
The naming of keys is ignored.
321
 
322
``hasKey()`` matches any argument which is an array containing the given key
323
name:
324
 
325
.. code-block:: php
326
 
327
    $mock = \Mockery::mock('MyClass');
328
    $mock->shouldReceive('foo')
329
        ->with(\Mockery::hasKey(key));
330
 
331
``hasValue()`` matches any argument which is an array containing the given
332
value:
333
 
334
.. code-block:: php
335
 
336
    $mock = \Mockery::mock('MyClass');
337
    $mock->shouldReceive('foo')
338
        ->with(\Mockery::hasValue(value));