| 148 |
lars |
1 |
.. index::
|
|
|
2 |
single: Reference; Creating Test Doubles
|
|
|
3 |
|
|
|
4 |
Creating Test Doubles
|
|
|
5 |
=====================
|
|
|
6 |
|
|
|
7 |
Mockery's main goal is to help us create test doubles. It can create stubs,
|
|
|
8 |
mocks, and spies.
|
|
|
9 |
|
|
|
10 |
Stubs and mocks are created the same. The difference between the two is that a
|
|
|
11 |
stub only returns a preset result when called, while a mock needs to have
|
|
|
12 |
expectations set on the method calls it expects to receive.
|
|
|
13 |
|
|
|
14 |
Spies are a type of test doubles that keep track of the calls they received, and
|
|
|
15 |
allow us to inspect these calls after the fact.
|
|
|
16 |
|
|
|
17 |
When creating a test double object, we can pass in an identifier as a name for
|
|
|
18 |
our test double. If we pass it no identifier, the test double name will be
|
|
|
19 |
unknown. Furthermore, the identifier does not have to be a class name. It is a
|
|
|
20 |
good practice, and our recommendation, to always name the test doubles with the
|
|
|
21 |
same name as the underlying class we are creating test doubles for.
|
|
|
22 |
|
|
|
23 |
If the identifier we use for our test double is a name of an existing class,
|
|
|
24 |
the test double will inherit the type of the class (via inheritance), i.e. the
|
|
|
25 |
mock object will pass type hints or ``instanceof`` evaluations for the existing
|
|
|
26 |
class. This is useful when a test double must be of a specific type, to satisfy
|
|
|
27 |
the expectations our code has.
|
|
|
28 |
|
|
|
29 |
Stubs and mocks
|
|
|
30 |
---------------
|
|
|
31 |
|
|
|
32 |
Stubs and mocks are created by calling the ``\Mockery::mock()`` method. The
|
|
|
33 |
following example shows how to create a stub, or a mock, object named "foo":
|
|
|
34 |
|
|
|
35 |
.. code-block:: php
|
|
|
36 |
|
|
|
37 |
$mock = \Mockery::mock('foo');
|
|
|
38 |
|
|
|
39 |
The mock object created like this is the loosest form of mocks possible, and is
|
|
|
40 |
an instance of ``\Mockery\MockInterface``.
|
|
|
41 |
|
|
|
42 |
.. note::
|
|
|
43 |
|
|
|
44 |
All test doubles created with Mockery are an instance of
|
|
|
45 |
``\Mockery\MockInterface``, regardless are they a stub, mock or a spy.
|
|
|
46 |
|
|
|
47 |
To create a stub or a mock object with no name, we can call the ``mock()``
|
|
|
48 |
method with no parameters:
|
|
|
49 |
|
|
|
50 |
.. code-block:: php
|
|
|
51 |
|
|
|
52 |
$mock = \Mockery::mock();
|
|
|
53 |
|
|
|
54 |
As we stated earlier, we don't recommend creating stub or mock objects without
|
|
|
55 |
a name.
|
|
|
56 |
|
|
|
57 |
Classes, abstracts, interfaces
|
|
|
58 |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
59 |
|
|
|
60 |
The recommended way to create a stub or a mock object is by using a name of
|
|
|
61 |
an existing class we want to create a test double of:
|
|
|
62 |
|
|
|
63 |
.. code-block:: php
|
|
|
64 |
|
|
|
65 |
$mock = \Mockery::mock('MyClass');
|
|
|
66 |
|
|
|
67 |
This stub or mock object will have the type of ``MyClass``, through inheritance.
|
|
|
68 |
|
|
|
69 |
Stub or mock objects can be based on any concrete class, abstract class or even
|
|
|
70 |
an interface. The primary purpose is to ensure the mock object inherits a
|
|
|
71 |
specific type for type hinting.
|
|
|
72 |
|
|
|
73 |
.. code-block:: php
|
|
|
74 |
|
|
|
75 |
$mock = \Mockery::mock('MyInterface');
|
|
|
76 |
|
|
|
77 |
This stub or mock object will implement the ``MyInterface`` interface.
|
|
|
78 |
|
|
|
79 |
.. note::
|
|
|
80 |
|
|
|
81 |
Classes marked final, or classes that have methods marked final cannot be
|
|
|
82 |
mocked fully. Mockery supports creating partial mocks for these cases.
|
|
|
83 |
Partial mocks will be explained later in the documentation.
|
|
|
84 |
|
|
|
85 |
Mockery also supports creating stub or mock objects based on a single existing
|
|
|
86 |
class, which must implement one or more interfaces. We can do this by providing
|
|
|
87 |
a comma-separated list of the class and interfaces as the first argument to the
|
|
|
88 |
``\Mockery::mock()`` method:
|
|
|
89 |
|
|
|
90 |
.. code-block:: php
|
|
|
91 |
|
|
|
92 |
$mock = \Mockery::mock('MyClass, MyInterface, OtherInterface');
|
|
|
93 |
|
|
|
94 |
This stub or mock object will now be of type ``MyClass`` and implement the
|
|
|
95 |
``MyInterface`` and ``OtherInterface`` interfaces.
|
|
|
96 |
|
|
|
97 |
.. note::
|
|
|
98 |
|
|
|
99 |
The class name doesn't need to be the first member of the list but it's a
|
|
|
100 |
friendly convention to use for readability.
|
|
|
101 |
|
|
|
102 |
We can tell a mock to implement the desired interfaces by passing the list of
|
|
|
103 |
interfaces as the second argument:
|
|
|
104 |
|
|
|
105 |
.. code-block:: php
|
|
|
106 |
|
|
|
107 |
$mock = \Mockery::mock('MyClass', 'MyInterface, OtherInterface');
|
|
|
108 |
|
|
|
109 |
For all intents and purposes, this is the same as the previous example.
|
|
|
110 |
|
|
|
111 |
Spies
|
|
|
112 |
-----
|
|
|
113 |
|
|
|
114 |
The third type of test doubles Mockery supports are spies. The main difference
|
|
|
115 |
between spies and mock objects is that with spies we verify the calls made
|
|
|
116 |
against our test double after the calls were made. We would use a spy when we
|
|
|
117 |
don't necessarily care about all of the calls that are going to be made to an
|
|
|
118 |
object.
|
|
|
119 |
|
|
|
120 |
A spy will return ``null`` for all method calls it receives. It is not possible
|
|
|
121 |
to tell a spy what will be the return value of a method call. If we do that, then
|
|
|
122 |
we would deal with a mock object, and not with a spy.
|
|
|
123 |
|
|
|
124 |
We create a spy by calling the ``\Mockery::spy()`` method:
|
|
|
125 |
|
|
|
126 |
.. code-block:: php
|
|
|
127 |
|
|
|
128 |
$spy = \Mockery::spy('MyClass');
|
|
|
129 |
|
|
|
130 |
Just as with stubs or mocks, we can tell Mockery to base a spy on any concrete
|
|
|
131 |
or abstract class, or to implement any number of interfaces:
|
|
|
132 |
|
|
|
133 |
.. code-block:: php
|
|
|
134 |
|
|
|
135 |
$spy = \Mockery::spy('MyClass, MyInterface, OtherInterface');
|
|
|
136 |
|
|
|
137 |
This spy will now be of type ``MyClass`` and implement the ``MyInterface`` and
|
|
|
138 |
``OtherInterface`` interfaces.
|
|
|
139 |
|
|
|
140 |
.. note::
|
|
|
141 |
|
|
|
142 |
The ``\Mockery::spy()`` method call is actually a shorthand for calling
|
|
|
143 |
``\Mockery::mock()->shouldIgnoreMissing()``. The ``shouldIgnoreMissing``
|
|
|
144 |
method is a "behaviour modifier". We'll discuss them a bit later.
|
|
|
145 |
|
|
|
146 |
Mocks vs. Spies
|
|
|
147 |
---------------
|
|
|
148 |
|
|
|
149 |
Let's try and illustrate the difference between mocks and spies with the
|
|
|
150 |
following example:
|
|
|
151 |
|
|
|
152 |
.. code-block:: php
|
|
|
153 |
|
|
|
154 |
$mock = \Mockery::mock('MyClass');
|
|
|
155 |
$spy = \Mockery::spy('MyClass');
|
|
|
156 |
|
|
|
157 |
$mock->shouldReceive('foo')->andReturn(42);
|
|
|
158 |
|
|
|
159 |
$mockResult = $mock->foo();
|
|
|
160 |
$spyResult = $spy->foo();
|
|
|
161 |
|
|
|
162 |
$spy->shouldHaveReceived()->foo();
|
|
|
163 |
|
|
|
164 |
var_dump($mockResult); // int(42)
|
|
|
165 |
var_dump($spyResult); // null
|
|
|
166 |
|
|
|
167 |
As we can see from this example, with a mock object we set the call expectations
|
|
|
168 |
before the call itself, and we get the return result we expect it to return.
|
|
|
169 |
With a spy object on the other hand, we verify the call has happened after the
|
|
|
170 |
fact. The return result of a method call against a spy is always ``null``.
|
|
|
171 |
|
|
|
172 |
We also have a dedicated chapter to :doc:`spies` only.
|
|
|
173 |
|
|
|
174 |
.. _creating-test-doubles-partial-test-doubles:
|
|
|
175 |
|
|
|
176 |
Partial Test Doubles
|
|
|
177 |
--------------------
|
|
|
178 |
|
|
|
179 |
Partial doubles are useful when we want to stub out, set expectations for, or
|
|
|
180 |
spy on *some* methods of a class, but run the actual code for other methods.
|
|
|
181 |
|
|
|
182 |
We differentiate between three types of partial test doubles:
|
|
|
183 |
|
|
|
184 |
* runtime partial test doubles,
|
|
|
185 |
* generated partial test doubles, and
|
|
|
186 |
* proxied partial test doubles.
|
|
|
187 |
|
|
|
188 |
Runtime partial test doubles
|
|
|
189 |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
190 |
|
|
|
191 |
What we call a runtime partial, involves creating a test double and then telling
|
|
|
192 |
it to make itself partial. Any method calls that the double hasn't been told to
|
|
|
193 |
allow or expect, will act as they would on a normal instance of the object.
|
|
|
194 |
|
|
|
195 |
.. code-block:: php
|
|
|
196 |
|
|
|
197 |
class Foo {
|
|
|
198 |
function foo() { return 123; }
|
|
|
199 |
function bar() { return $this->foo(); }
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
$foo = mock(Foo::class)->makePartial();
|
|
|
203 |
$foo->foo(); // int(123);
|
|
|
204 |
|
|
|
205 |
We can then tell the test double to allow or expect calls as with any other
|
|
|
206 |
Mockery double.
|
|
|
207 |
|
|
|
208 |
.. code-block:: php
|
|
|
209 |
|
|
|
210 |
$foo->shouldReceive('foo')->andReturn(456);
|
|
|
211 |
$foo->bar(); // int(456)
|
|
|
212 |
|
|
|
213 |
See the cookbook entry on :doc:`../cookbook/big_parent_class` for an example
|
|
|
214 |
usage of runtime partial test doubles.
|
|
|
215 |
|
|
|
216 |
Generated partial test doubles
|
|
|
217 |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
218 |
|
|
|
219 |
The second type of partial double we can create is what we call a generated
|
|
|
220 |
partial. With generated partials, we specifically tell Mockery which methods
|
|
|
221 |
we want to be able to allow or expect calls to. All other methods will run the
|
|
|
222 |
actual code *directly*, so stubs and expectations on these methods will not
|
|
|
223 |
work.
|
|
|
224 |
|
|
|
225 |
.. code-block:: php
|
|
|
226 |
|
|
|
227 |
class Foo {
|
|
|
228 |
function foo() { return 123; }
|
|
|
229 |
function bar() { return $this->foo(); }
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
$foo = mock("Foo[foo]");
|
|
|
233 |
|
|
|
234 |
$foo->foo(); // error, no expectation set
|
|
|
235 |
|
|
|
236 |
$foo->shouldReceive('foo')->andReturn(456);
|
|
|
237 |
$foo->foo(); // int(456)
|
|
|
238 |
|
|
|
239 |
// setting an expectation for this has no effect
|
|
|
240 |
$foo->shouldReceive('bar')->andReturn(999);
|
|
|
241 |
$foo->bar(); // int(456)
|
|
|
242 |
|
|
|
243 |
It's also possible to specify explicitly which methods to run directly using
|
|
|
244 |
the `!method` syntax:
|
|
|
245 |
|
|
|
246 |
.. code-block:: php
|
|
|
247 |
|
|
|
248 |
class Foo {
|
|
|
249 |
function foo() { return 123; }
|
|
|
250 |
function bar() { return $this->foo(); }
|
|
|
251 |
}
|
|
|
252 |
|
|
|
253 |
$foo = mock("Foo[!foo]");
|
|
|
254 |
|
|
|
255 |
$foo->foo(); // int(123)
|
|
|
256 |
|
|
|
257 |
$foo->bar(); // error, no expectation set
|
|
|
258 |
|
|
|
259 |
.. note::
|
|
|
260 |
|
|
|
261 |
Even though we support generated partial test doubles, we do not recommend
|
|
|
262 |
using them.
|
|
|
263 |
|
|
|
264 |
One of the reasons why is because a generated partial will call the original
|
|
|
265 |
constructor of the mocked class. This can have unwanted side-effects during
|
|
|
266 |
testing application code.
|
|
|
267 |
|
|
|
268 |
See :doc:`../cookbook/not_calling_the_constructor` for more details.
|
|
|
269 |
|
|
|
270 |
Proxied partial test doubles
|
|
|
271 |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
272 |
|
|
|
273 |
A proxied partial mock is a partial of last resort. We may encounter a class
|
|
|
274 |
which is simply not capable of being mocked because it has been marked as
|
|
|
275 |
final. Similarly, we may find a class with methods marked as final. In such a
|
|
|
276 |
scenario, we cannot simply extend the class and override methods to mock - we
|
|
|
277 |
need to get creative.
|
|
|
278 |
|
|
|
279 |
.. code-block:: php
|
|
|
280 |
|
|
|
281 |
$mock = \Mockery::mock(new MyClass);
|
|
|
282 |
|
|
|
283 |
Yes, the new mock is a Proxy. It intercepts calls and reroutes them to the
|
|
|
284 |
proxied object (which we construct and pass in) for methods which are not
|
|
|
285 |
subject to any expectations. Indirectly, this allows us to mock methods
|
|
|
286 |
marked final since the Proxy is not subject to those limitations. The tradeoff
|
|
|
287 |
should be obvious - a proxied partial will fail any typehint checks for the
|
|
|
288 |
class being mocked since it cannot extend that class.
|
|
|
289 |
|
|
|
290 |
.. _creating-test-doubles-aliasing:
|
|
|
291 |
|
|
|
292 |
Aliasing
|
|
|
293 |
--------
|
|
|
294 |
|
|
|
295 |
Prefixing the valid name of a class (which is NOT currently loaded) with
|
|
|
296 |
"alias:" will generate an "alias mock". Alias mocks create a class alias with
|
|
|
297 |
the given classname to stdClass and are generally used to enable the mocking
|
|
|
298 |
of public static methods. Expectations set on the new mock object which refer
|
|
|
299 |
to static methods will be used by all static calls to this class.
|
|
|
300 |
|
|
|
301 |
.. code-block:: php
|
|
|
302 |
|
|
|
303 |
$mock = \Mockery::mock('alias:MyClass');
|
|
|
304 |
|
|
|
305 |
|
|
|
306 |
.. note::
|
|
|
307 |
|
|
|
308 |
Even though aliasing classes is supported, we do not recommend it.
|
|
|
309 |
|
|
|
310 |
Overloading
|
|
|
311 |
-----------
|
|
|
312 |
|
|
|
313 |
Prefixing the valid name of a class (which is NOT currently loaded) with
|
|
|
314 |
"overload:" will generate an alias mock (as with "alias:") except that created
|
|
|
315 |
new instances of that class will import any expectations set on the origin
|
|
|
316 |
mock (``$mock``). The origin mock is never verified since it's used an
|
|
|
317 |
expectation store for new instances. For this purpose we use the term "instance
|
|
|
318 |
mock" to differentiate it from the simpler "alias mock".
|
|
|
319 |
|
|
|
320 |
In other words, an instance mock will "intercept" when a new instance of the
|
|
|
321 |
mocked class is created, then the mock will be used instead. This is useful
|
|
|
322 |
especially when mocking hard dependencies which will be discussed later.
|
|
|
323 |
|
|
|
324 |
.. code-block:: php
|
|
|
325 |
|
|
|
326 |
$mock = \Mockery::mock('overload:MyClass');
|
|
|
327 |
|
|
|
328 |
.. note::
|
|
|
329 |
|
|
|
330 |
Using alias/instance mocks across more than one test will generate a fatal
|
|
|
331 |
error since we can't have two classes of the same name. To avoid this,
|
|
|
332 |
run each test of this kind in a separate PHP process (which is supported
|
|
|
333 |
out of the box by both PHPUnit and PHPT).
|
|
|
334 |
|
|
|
335 |
|
|
|
336 |
.. _creating-test-doubles-named-mocks:
|
|
|
337 |
|
|
|
338 |
Named Mocks
|
|
|
339 |
-----------
|
|
|
340 |
|
|
|
341 |
The ``namedMock()`` method will generate a class called by the first argument,
|
|
|
342 |
so in this example ``MyClassName``. The rest of the arguments are treated in the
|
|
|
343 |
same way as the ``mock`` method:
|
|
|
344 |
|
|
|
345 |
.. code-block:: php
|
|
|
346 |
|
|
|
347 |
$mock = \Mockery::namedMock('MyClassName', 'DateTime');
|
|
|
348 |
|
|
|
349 |
This example would create a class called ``MyClassName`` that extends
|
|
|
350 |
``DateTime``.
|
|
|
351 |
|
|
|
352 |
Named mocks are quite an edge case, but they can be useful when code depends
|
|
|
353 |
on the ``__CLASS__`` magic constant, or when we need two derivatives of an
|
|
|
354 |
abstract type, that are actually different classes.
|
|
|
355 |
|
|
|
356 |
See the cookbook entry on :doc:`../cookbook/class_constants` for an example
|
|
|
357 |
usage of named mocks.
|
|
|
358 |
|
|
|
359 |
.. note::
|
|
|
360 |
|
|
|
361 |
We can only create a named mock once, any subsequent calls to
|
|
|
362 |
``namedMock``, with different arguments are likely to cause exceptions.
|
|
|
363 |
|
|
|
364 |
.. _creating-test-doubles-constructor-arguments:
|
|
|
365 |
|
|
|
366 |
Constructor Arguments
|
|
|
367 |
---------------------
|
|
|
368 |
|
|
|
369 |
Sometimes the mocked class has required constructor arguments. We can pass these
|
|
|
370 |
to Mockery as an indexed array, as the 2nd argument:
|
|
|
371 |
|
|
|
372 |
.. code-block:: php
|
|
|
373 |
|
|
|
374 |
$mock = \Mockery::mock('MyClass', [$constructorArg1, $constructorArg2]);
|
|
|
375 |
|
|
|
376 |
or if we need the ``MyClass`` to implement an interface as well, as the 3rd
|
|
|
377 |
argument:
|
|
|
378 |
|
|
|
379 |
.. code-block:: php
|
|
|
380 |
|
|
|
381 |
$mock = \Mockery::mock('MyClass', 'MyInterface', [$constructorArg1, $constructorArg2]);
|
|
|
382 |
|
|
|
383 |
Mockery now knows to pass in ``$constructorArg1`` and ``$constructorArg2`` as
|
|
|
384 |
arguments to the constructor.
|
|
|
385 |
|
|
|
386 |
.. _creating-test-doubles-behavior-modifiers:
|
|
|
387 |
|
|
|
388 |
Behavior Modifiers
|
|
|
389 |
------------------
|
|
|
390 |
|
|
|
391 |
When creating a mock object, we may wish to use some commonly preferred
|
|
|
392 |
behaviours that are not the default in Mockery.
|
|
|
393 |
|
|
|
394 |
The use of the ``shouldIgnoreMissing()`` behaviour modifier will label this
|
|
|
395 |
mock object as a Passive Mock:
|
|
|
396 |
|
|
|
397 |
.. code-block:: php
|
|
|
398 |
|
|
|
399 |
\Mockery::mock('MyClass')->shouldIgnoreMissing();
|
|
|
400 |
|
|
|
401 |
In such a mock object, calls to methods which are not covered by expectations
|
|
|
402 |
will return ``null`` instead of the usual error about there being no expectation
|
|
|
403 |
matching the call.
|
|
|
404 |
|
|
|
405 |
On PHP >= 7.0.0, methods with missing expectations that have a return type
|
|
|
406 |
will return either a mock of the object (if return type is a class) or a
|
|
|
407 |
"falsy" primitive value, e.g. empty string, empty array, zero for ints and
|
|
|
408 |
floats, false for bools, or empty closures.
|
|
|
409 |
|
|
|
410 |
On PHP >= 7.1.0, methods with missing expectations and nullable return type
|
|
|
411 |
will return null.
|
|
|
412 |
|
|
|
413 |
We can optionally prefer to return an object of type ``\Mockery\Undefined``
|
|
|
414 |
(i.e. a ``null`` object) (which was the 0.7.2 behaviour) by using an
|
|
|
415 |
additional modifier:
|
|
|
416 |
|
|
|
417 |
.. code-block:: php
|
|
|
418 |
|
|
|
419 |
\Mockery::mock('MyClass')->shouldIgnoreMissing()->asUndefined();
|
|
|
420 |
|
|
|
421 |
The returned object is nothing more than a placeholder so if, by some act of
|
|
|
422 |
fate, it's erroneously used somewhere it shouldn't it will likely not pass a
|
|
|
423 |
logic check.
|
|
|
424 |
|
|
|
425 |
We have encountered the ``makePartial()`` method before, as it is the method we
|
|
|
426 |
use to create runtime partial test doubles:
|
|
|
427 |
|
|
|
428 |
.. code-block:: php
|
|
|
429 |
|
|
|
430 |
\Mockery::mock('MyClass')->makePartial();
|
|
|
431 |
|
|
|
432 |
This form of mock object will defer all methods not subject to an expectation to
|
|
|
433 |
the parent class of the mock, i.e. ``MyClass``. Whereas the previous
|
|
|
434 |
``shouldIgnoreMissing()`` returned ``null``, this behaviour simply calls the
|
|
|
435 |
parent's matching method.
|