Fix bin/publish: copy docs.dist from project root

Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
.. index::
single: Alternative shouldReceive Syntax
Alternative shouldReceive Syntax
================================
As of Mockery 1.0.0, we support calling methods as we would call any PHP method,
and not as string arguments to Mockery ``should*`` methods.
The two Mockery methods that enable this are ``allows()`` and ``expects()``.
Allows
------
We use ``allows()`` when we create stubs for methods that return a predefined
return value, but for these method stubs we don't care how many times, or if at
all, were they called.
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->allows([
'name_of_method_1' => 'return value',
'name_of_method_2' => 'return value',
]);
This is equivalent with the following ``shouldReceive`` syntax:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive([
'name_of_method_1' => 'return value',
'name_of_method_2' => 'return value',
]);
Note that with this format, we also tell Mockery that we don't care about the
arguments to the stubbed methods.
If we do care about the arguments, we would do it like so:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->allows()
->name_of_method_1($arg1)
->andReturn('return value');
This is equivalent with the following ``shouldReceive`` syntax:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method_1')
->with($arg1)
->andReturn('return value');
Expects
-------
We use ``expects()`` when we want to verify that a particular method was called:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->expects()
->name_of_method_1($arg1)
->andReturn('return value');
This is equivalent with the following ``shouldReceive`` syntax:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method_1')
->once()
->with($arg1)
->andReturn('return value');
By default ``expects()`` sets up an expectation that the method should be called
once and once only. If we expect more than one call to the method, we can change
that expectation:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->expects()
->name_of_method_1($arg1)
->twice()
->andReturn('return value');

View File

@@ -0,0 +1,338 @@
.. index::
single: Argument Validation
Argument Validation
===================
The arguments passed to the ``with()`` declaration when setting up an
expectation determine the criteria for matching method calls to expectations.
Thus, we can setup up many expectations for a single method, each
differentiated by the expected arguments. Such argument matching is done on a
"best fit" basis. This ensures explicit matches take precedence over
generalised matches.
An explicit match is merely where the expected argument and the actual
argument are easily equated (i.e. using ``===`` or ``==``). More generalised
matches are possible using regular expressions, class hinting and the
available generic matchers. The purpose of generalised matchers is to allow
arguments be defined in non-explicit terms, e.g. ``Mockery::any()`` passed to
``with()`` will match **any** argument in that position.
Mockery's generic matchers do not cover all possibilities but offers optional
support for the Hamcrest library of matchers. Hamcrest is a PHP port of the
similarly named Java library (which has been ported also to Python, Erlang,
etc). By using Hamcrest, Mockery does not need to duplicate Hamcrest's already
impressive utility which itself promotes a natural English DSL.
The examples below show Mockery matchers and their Hamcrest equivalent, if there
is one. Hamcrest uses functions (no namespacing).
.. note::
If you don't wish to use the global Hamcrest functions, they are all exposed
through the ``\Hamcrest\Matchers`` class as well, as static methods. Thus,
``identicalTo($arg)`` is the same as ``\Hamcrest\Matchers::identicalTo($arg)``
The most common matcher is the ``with()`` matcher:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(1):
It tells mockery that it should receive a call to the ``foo`` method with the
integer ``1`` as an argument. In cases like this, Mockery first tries to match
the arguments using ``===`` (identical) comparison operator. If the argument is
a primitive, and if it fails the identical comparison, Mockery does a fallback
to the ``==`` (equals) comparison operator.
When matching objects as arguments, Mockery only does the strict ``===``
comparison, which means only the same ``$object`` will match:
.. code-block:: php
$object = new stdClass();
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive("foo")
->with($object);
// Hamcrest equivalent
$mock->shouldReceive("foo")
->with(identicalTo($object));
A different instance of ``stdClass`` will **not** match.
.. note::
The ``Mockery\Matcher\MustBe`` matcher has been deprecated.
If we need a loose comparison of objects, we can do that using Hamcrest's
``equalTo`` matcher:
.. code-block:: php
$mock->shouldReceive("foo")
->with(equalTo(new stdClass));
In cases when we don't care about the type, or the value of an argument, just
that any argument is present, we use ``any()``:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive("foo")
->with(\Mockery::any());
// Hamcrest equivalent
$mock->shouldReceive("foo")
->with(anything())
Anything and everything passed in this argument slot is passed unconstrained.
Validating Types and Resources
------------------------------
The ``type()`` matcher accepts any string which can be attached to ``is_`` to
form a valid type check.
To match any PHP resource, we could do the following:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive("foo")
->with(\Mockery::type('resource'));
// Hamcrest equivalents
$mock->shouldReceive("foo")
->with(resourceValue());
$mock->shouldReceive("foo")
->with(typeOf('resource'));
It will return a ``true`` from an ``is_resource()`` call, if the provided
argument to the method is a PHP resource. For example, ``\Mockery::type('float')``
or Hamcrest's ``floatValue()`` and ``typeOf('float')`` checks use ``is_float()``,
and ``\Mockery::type('callable')`` or Hamcrest's ``callable()`` uses
``is_callable()``.
The ``type()`` matcher also accepts a class or interface name to be used in an
``instanceof`` evaluation of the actual argument. Hamcrest uses ``anInstanceOf()``.
A full list of the type checkers is available at
`php.net <http://www.php.net/manual/en/ref.var.php>`_ or browse Hamcrest's function
list in
`the Hamcrest code <https://github.com/hamcrest/hamcrest-php/blob/master/hamcrest/Hamcrest.php>`_.
.. _argument-validation-complex-argument-validation:
Complex Argument Validation
---------------------------
If we want to perform a complex argument validation, the ``on()`` matcher is
invaluable. It accepts a closure (anonymous function) to which the actual
argument will be passed.
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive("foo")
->with(\Mockery::on(closure));
If the closure evaluates to (i.e. returns) boolean ``true`` then the argument is
assumed to have matched the expectation.
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::on(function ($argument) {
if ($argument % 2 == 0) {
return true;
}
return false;
}));
$mock->foo(4); // matches the expectation
$mock->foo(3); // throws a NoMatchingExpectationException
.. note::
There is no Hamcrest version of the ``on()`` matcher.
We can also perform argument validation by passing a closure to ``withArgs()``
method. The closure will receive all arguments passed in the call to the expected
method and if it evaluates (i.e. returns) to boolean ``true``, then the list of
arguments is assumed to have matched the expectation:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive("foo")
->withArgs(closure);
The closure can also handle optional parameters, so if an optional parameter is
missing in the call to the expected method, it doesn't necessary means that the
list of arguments doesn't match the expectation.
.. code-block:: php
$closure = function ($odd, $even, $sum = null) {
$result = ($odd % 2 != 0) && ($even % 2 == 0);
if (!is_null($sum)) {
return $result && ($odd + $even == $sum);
}
return $result;
};
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')->withArgs($closure);
$mock->foo(1, 2); // It matches the expectation: the optional argument is not needed
$mock->foo(1, 2, 3); // It also matches the expectation: the optional argument pass the validation
$mock->foo(1, 2, 4); // It doesn't match the expectation: the optional doesn't pass the validation
.. note::
In previous versions, Mockery's ``with()`` would attempt to do a pattern
matching against the arguments, attempting to use the argument as a
regular expression. Over time this proved to be not such a great idea, so
we removed this functionality, and have introduced ``Mockery::pattern()``
instead.
If we would like to match an argument against a regular expression, we can use
the ``\Mockery::pattern()``:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::pattern('/^foo/'));
// Hamcrest equivalent
$mock->shouldReceive('foo')
->with(matchesPattern('/^foo/'));
The ``ducktype()`` matcher is an alternative to matching by class type:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::ducktype('foo', 'bar'));
It matches any argument which is an object containing the provided list of
methods to call.
.. note::
There is no Hamcrest version of the ``ducktype()`` matcher.
Capturing Arguments
-------------------
If we want to perform multiple validations on a single argument, the ``capture``
matcher provides a streamlined alternative to using the ``on()`` matcher.
It accepts a variable which the actual argument will be assigned.
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive("foo")
->with(\Mockery::capture($bar));
This will assign *any* argument passed to ``foo`` to the local ``$bar`` variable to
then perform additional validation using assertions.
.. note::
The ``capture`` matcher always evaluates to ``true``. As such, we should always
perform additional argument validation.
Additional Argument Matchers
----------------------------
The ``not()`` matcher matches any argument which is not equal or identical to
the matcher's parameter:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::not(2));
// Hamcrest equivalent
$mock->shouldReceive('foo')
->with(not(2));
``anyOf()`` matches any argument which equals any one of the given parameters:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::anyOf(1, 2));
// Hamcrest equivalent
$mock->shouldReceive('foo')
->with(anyOf(1,2));
``notAnyOf()`` matches any argument which is not equal or identical to any of
the given parameters:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::notAnyOf(1, 2));
.. note::
There is no Hamcrest version of the ``notAnyOf()`` matcher.
``subset()`` matches any argument which is any array containing the given array
subset:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::subset(array(0 => 'foo')));
This enforces both key naming and values, i.e. both the key and value of each
actual element is compared.
.. note::
There is no Hamcrest version of this functionality, though Hamcrest can check
a single entry using ``hasEntry()`` or ``hasKeyValuePair()``.
``contains()`` matches any argument which is an array containing the listed
values:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::contains(value1, value2));
The naming of keys is ignored.
``hasKey()`` matches any argument which is an array containing the given key
name:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::hasKey(key));
``hasValue()`` matches any argument which is an array containing the given
value:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->with(\Mockery::hasValue(value));

View File

@@ -0,0 +1,435 @@
.. index::
single: Reference; Creating Test Doubles
Creating Test Doubles
=====================
Mockery's main goal is to help us create test doubles. It can create stubs,
mocks, and spies.
Stubs and mocks are created the same. The difference between the two is that a
stub only returns a preset result when called, while a mock needs to have
expectations set on the method calls it expects to receive.
Spies are a type of test doubles that keep track of the calls they received, and
allow us to inspect these calls after the fact.
When creating a test double object, we can pass in an identifier as a name for
our test double. If we pass it no identifier, the test double name will be
unknown. Furthermore, the identifier does not have to be a class name. It is a
good practice, and our recommendation, to always name the test doubles with the
same name as the underlying class we are creating test doubles for.
If the identifier we use for our test double is a name of an existing class,
the test double will inherit the type of the class (via inheritance), i.e. the
mock object will pass type hints or ``instanceof`` evaluations for the existing
class. This is useful when a test double must be of a specific type, to satisfy
the expectations our code has.
Stubs and mocks
---------------
Stubs and mocks are created by calling the ``\Mockery::mock()`` method. The
following example shows how to create a stub, or a mock, object named "foo":
.. code-block:: php
$mock = \Mockery::mock('foo');
The mock object created like this is the loosest form of mocks possible, and is
an instance of ``\Mockery\MockInterface``.
.. note::
All test doubles created with Mockery are an instance of
``\Mockery\MockInterface``, regardless are they a stub, mock or a spy.
To create a stub or a mock object with no name, we can call the ``mock()``
method with no parameters:
.. code-block:: php
$mock = \Mockery::mock();
As we stated earlier, we don't recommend creating stub or mock objects without
a name.
Classes, abstracts, interfaces
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The recommended way to create a stub or a mock object is by using a name of
an existing class we want to create a test double of:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
This stub or mock object will have the type of ``MyClass``, through inheritance.
Stub or mock objects can be based on any concrete class, abstract class or even
an interface. The primary purpose is to ensure the mock object inherits a
specific type for type hinting.
.. code-block:: php
$mock = \Mockery::mock('MyInterface');
This stub or mock object will implement the ``MyInterface`` interface.
.. note::
Classes marked final, or classes that have methods marked final cannot be
mocked fully. Mockery supports creating partial mocks for these cases.
Partial mocks will be explained later in the documentation.
Mockery also supports creating stub or mock objects based on a single existing
class, which must implement one or more interfaces. We can do this by providing
a comma-separated list of the class and interfaces as the first argument to the
``\Mockery::mock()`` method:
.. code-block:: php
$mock = \Mockery::mock('MyClass, MyInterface, OtherInterface');
This stub or mock object will now be of type ``MyClass`` and implement the
``MyInterface`` and ``OtherInterface`` interfaces.
.. note::
The class name doesn't need to be the first member of the list but it's a
friendly convention to use for readability.
We can tell a mock to implement the desired interfaces by passing the list of
interfaces as the second argument:
.. code-block:: php
$mock = \Mockery::mock('MyClass', 'MyInterface, OtherInterface');
For all intents and purposes, this is the same as the previous example.
Spies
-----
The third type of test doubles Mockery supports are spies. The main difference
between spies and mock objects is that with spies we verify the calls made
against our test double after the calls were made. We would use a spy when we
don't necessarily care about all of the calls that are going to be made to an
object.
A spy will return ``null`` for all method calls it receives. It is not possible
to tell a spy what will be the return value of a method call. If we do that, then
we would deal with a mock object, and not with a spy.
We create a spy by calling the ``\Mockery::spy()`` method:
.. code-block:: php
$spy = \Mockery::spy('MyClass');
Just as with stubs or mocks, we can tell Mockery to base a spy on any concrete
or abstract class, or to implement any number of interfaces:
.. code-block:: php
$spy = \Mockery::spy('MyClass, MyInterface, OtherInterface');
This spy will now be of type ``MyClass`` and implement the ``MyInterface`` and
``OtherInterface`` interfaces.
.. note::
The ``\Mockery::spy()`` method call is actually a shorthand for calling
``\Mockery::mock()->shouldIgnoreMissing()``. The ``shouldIgnoreMissing``
method is a "behaviour modifier". We'll discuss them a bit later.
Mocks vs. Spies
---------------
Let's try and illustrate the difference between mocks and spies with the
following example:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$spy = \Mockery::spy('MyClass');
$mock->shouldReceive('foo')->andReturn(42);
$mockResult = $mock->foo();
$spyResult = $spy->foo();
$spy->shouldHaveReceived()->foo();
var_dump($mockResult); // int(42)
var_dump($spyResult); // null
As we can see from this example, with a mock object we set the call expectations
before the call itself, and we get the return result we expect it to return.
With a spy object on the other hand, we verify the call has happened after the
fact. The return result of a method call against a spy is always ``null``.
We also have a dedicated chapter to :doc:`spies` only.
.. _creating-test-doubles-partial-test-doubles:
Partial Test Doubles
--------------------
Partial doubles are useful when we want to stub out, set expectations for, or
spy on *some* methods of a class, but run the actual code for other methods.
We differentiate between three types of partial test doubles:
* runtime partial test doubles,
* generated partial test doubles, and
* proxied partial test doubles.
Runtime partial test doubles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What we call a runtime partial, involves creating a test double and then telling
it to make itself partial. Any method calls that the double hasn't been told to
allow or expect, will act as they would on a normal instance of the object.
.. code-block:: php
class Foo {
function foo() { return 123; }
function bar() { return $this->foo(); }
}
$foo = mock(Foo::class)->makePartial();
$foo->foo(); // int(123);
We can then tell the test double to allow or expect calls as with any other
Mockery double.
.. code-block:: php
$foo->shouldReceive('foo')->andReturn(456);
$foo->bar(); // int(456)
See the cookbook entry on :doc:`../cookbook/big_parent_class` for an example
usage of runtime partial test doubles.
Generated partial test doubles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The second type of partial double we can create is what we call a generated
partial. With generated partials, we specifically tell Mockery which methods
we want to be able to allow or expect calls to. All other methods will run the
actual code *directly*, so stubs and expectations on these methods will not
work.
.. code-block:: php
class Foo {
function foo() { return 123; }
function bar() { return $this->foo(); }
}
$foo = mock("Foo[foo]");
$foo->foo(); // error, no expectation set
$foo->shouldReceive('foo')->andReturn(456);
$foo->foo(); // int(456)
// setting an expectation for this has no effect
$foo->shouldReceive('bar')->andReturn(999);
$foo->bar(); // int(456)
It's also possible to specify explicitly which methods to run directly using
the `!method` syntax:
.. code-block:: php
class Foo {
function foo() { return 123; }
function bar() { return $this->foo(); }
}
$foo = mock("Foo[!foo]");
$foo->foo(); // int(123)
$foo->bar(); // error, no expectation set
.. note::
Even though we support generated partial test doubles, we do not recommend
using them.
One of the reasons why is because a generated partial will call the original
constructor of the mocked class. This can have unwanted side-effects during
testing application code.
See :doc:`../cookbook/not_calling_the_constructor` for more details.
Proxied partial test doubles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A proxied partial mock is a partial of last resort. We may encounter a class
which is simply not capable of being mocked because it has been marked as
final. Similarly, we may find a class with methods marked as final. In such a
scenario, we cannot simply extend the class and override methods to mock - we
need to get creative.
.. code-block:: php
$mock = \Mockery::mock(new MyClass);
Yes, the new mock is a Proxy. It intercepts calls and reroutes them to the
proxied object (which we construct and pass in) for methods which are not
subject to any expectations. Indirectly, this allows us to mock methods
marked final since the Proxy is not subject to those limitations. The tradeoff
should be obvious - a proxied partial will fail any typehint checks for the
class being mocked since it cannot extend that class.
.. _creating-test-doubles-aliasing:
Aliasing
--------
Prefixing the valid name of a class (which is NOT currently loaded) with
"alias:" will generate an "alias mock". Alias mocks create a class alias with
the given classname to stdClass and are generally used to enable the mocking
of public static methods. Expectations set on the new mock object which refer
to static methods will be used by all static calls to this class.
.. code-block:: php
$mock = \Mockery::mock('alias:MyClass');
.. note::
Even though aliasing classes is supported, we do not recommend it.
Overloading
-----------
Prefixing the valid name of a class (which is NOT currently loaded) with
"overload:" will generate an alias mock (as with "alias:") except that created
new instances of that class will import any expectations set on the origin
mock (``$mock``). The origin mock is never verified since it's used an
expectation store for new instances. For this purpose we use the term "instance
mock" to differentiate it from the simpler "alias mock".
In other words, an instance mock will "intercept" when a new instance of the
mocked class is created, then the mock will be used instead. This is useful
especially when mocking hard dependencies which will be discussed later.
.. code-block:: php
$mock = \Mockery::mock('overload:MyClass');
.. note::
Using alias/instance mocks across more than one test will generate a fatal
error since we can't have two classes of the same name. To avoid this,
run each test of this kind in a separate PHP process (which is supported
out of the box by both PHPUnit and PHPT).
.. _creating-test-doubles-named-mocks:
Named Mocks
-----------
The ``namedMock()`` method will generate a class called by the first argument,
so in this example ``MyClassName``. The rest of the arguments are treated in the
same way as the ``mock`` method:
.. code-block:: php
$mock = \Mockery::namedMock('MyClassName', 'DateTime');
This example would create a class called ``MyClassName`` that extends
``DateTime``.
Named mocks are quite an edge case, but they can be useful when code depends
on the ``__CLASS__`` magic constant, or when we need two derivatives of an
abstract type, that are actually different classes.
See the cookbook entry on :doc:`../cookbook/class_constants` for an example
usage of named mocks.
.. note::
We can only create a named mock once, any subsequent calls to
``namedMock``, with different arguments are likely to cause exceptions.
.. _creating-test-doubles-constructor-arguments:
Constructor Arguments
---------------------
Sometimes the mocked class has required constructor arguments. We can pass these
to Mockery as an indexed array, as the 2nd argument:
.. code-block:: php
$mock = \Mockery::mock('MyClass', [$constructorArg1, $constructorArg2]);
or if we need the ``MyClass`` to implement an interface as well, as the 3rd
argument:
.. code-block:: php
$mock = \Mockery::mock('MyClass', 'MyInterface', [$constructorArg1, $constructorArg2]);
Mockery now knows to pass in ``$constructorArg1`` and ``$constructorArg2`` as
arguments to the constructor.
.. _creating-test-doubles-behavior-modifiers:
Behavior Modifiers
------------------
When creating a mock object, we may wish to use some commonly preferred
behaviours that are not the default in Mockery.
The use of the ``shouldIgnoreMissing()`` behaviour modifier will label this
mock object as a Passive Mock:
.. code-block:: php
\Mockery::mock('MyClass')->shouldIgnoreMissing();
In such a mock object, calls to methods which are not covered by expectations
will return ``null`` instead of the usual error about there being no expectation
matching the call.
On PHP >= 7.0.0, methods with missing expectations that have a return type
will return either a mock of the object (if return type is a class) or a
"falsy" primitive value, e.g. empty string, empty array, zero for ints and
floats, false for bools, or empty closures.
On PHP >= 7.1.0, methods with missing expectations and nullable return type
will return null.
We can optionally prefer to return an object of type ``\Mockery\Undefined``
(i.e. a ``null`` object) (which was the 0.7.2 behaviour) by using an
additional modifier:
.. code-block:: php
\Mockery::mock('MyClass')->shouldIgnoreMissing()->asUndefined();
The returned object is nothing more than a placeholder so if, by some act of
fate, it's erroneously used somewhere it shouldn't, it will likely not pass a
logic check.
We have encountered the ``makePartial()`` method before, as it is the method we
use to create runtime partial test doubles:
.. code-block:: php
\Mockery::mock('MyClass')->makePartial();
This form of mock object will defer all methods not subject to an expectation to
the parent class of the mock, i.e. ``MyClass``. Whereas the previous
``shouldIgnoreMissing()`` returned ``null``, this behaviour simply calls the
parent's matching method.

View File

@@ -0,0 +1,38 @@
.. index::
single: Mocking; Demeter Chains
Mocking Demeter Chains And Fluent Interfaces
============================================
Both of these terms refer to the growing practice of invoking statements
similar to:
.. code-block:: php
$object->foo()->bar()->zebra()->alpha()->selfDestruct();
The long chain of method calls isn't necessarily a bad thing, assuming they
each link back to a local object the calling class knows. As a fun example,
Mockery's long chains (after the first ``shouldReceive()`` method) all call to
the same instance of ``\Mockery\Expectation``. However, sometimes this is not
the case and the chain is constantly crossing object boundaries.
In either case, mocking such a chain can be a horrible task. To make it easier
Mockery supports demeter chain mocking. Essentially, we shortcut through the
chain and return a defined value from the final call. For example, let's
assume ``selfDestruct()`` returns the string "Ten!" to $object (an instance of
``CaptainsConsole``). Here's how we could mock it.
.. code-block:: php
$mock = \Mockery::mock('CaptainsConsole');
$mock->shouldReceive('foo->bar->zebra->alpha->selfDestruct')->andReturn('Ten!');
The above expectation can follow any previously seen format or expectation,
except that the method name is simply the string of all expected chain calls
separated by ``->``. Mockery will automatically setup the chain of expected
calls with its final return values, regardless of whatever intermediary object
might be used in the real implementation.
Arguments to all members of the chain (except the final call) are ignored in
this process.

View File

@@ -0,0 +1,533 @@
.. index::
single: Expectations
Expectation Declarations
========================
.. note::
In order for our expectations to work we MUST call ``Mockery::close()``,
preferably in a callback method such as ``tearDown`` or ``_after``
(depending on whether or not we're integrating Mockery with another
framework). This static call cleans up the Mockery container used by the
current test, and run any verification tasks needed for our expectations.
Once we have created a mock object, we'll often want to start defining how
exactly it should behave (and how it should be called). This is where the
Mockery expectation declarations take over.
Declaring Method Call Expectations
----------------------------------
To tell our test double to expect a call for a method with a given name, we use
the ``shouldReceive`` method:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method');
This is the starting expectation upon which all other expectations and
constraints are appended.
We can declare more than one method call to be expected:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method_1', 'name_of_method_2');
All of these will adopt any chained expectations or constraints.
It is possible to declare the expectations for the method calls, along with
their return values:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive([
'name_of_method_1' => 'return value 1',
'name_of_method_2' => 'return value 2',
]);
There's also a shorthand way of setting up method call expectations and their
return values:
.. code-block:: php
$mock = \Mockery::mock('MyClass', ['name_of_method_1' => 'return value 1', 'name_of_method_2' => 'return value 2']);
All of these will adopt any additional chained expectations or constraints.
We can declare that a test double should not expect a call to the given method
name:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldNotReceive('name_of_method');
This method is a convenience method for calling ``shouldReceive()->never()``.
Declaring Method Argument Expectations
--------------------------------------
For every method we declare expectation for, we can add constraints that the
defined expectations apply only to the method calls that match the expected
argument list:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->with($arg1, $arg2, ...);
// or
$mock->shouldReceive('name_of_method')
->withArgs([$arg1, $arg2, ...]);
We can add a lot more flexibility to argument matching using the built in
matcher classes (see later). For example, ``\Mockery::any()`` matches any
argument passed to that position in the ``with()`` parameter list. Mockery also
allows Hamcrest library matchers - for example, the Hamcrest function
``anything()`` is equivalent to ``\Mockery::any()``.
It's important to note that this means all expectations attached only apply to
the given method when it is called with these exact arguments:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')->with('Hello');
$mock->foo('Goodbye'); // throws a NoMatchingExpectationException
This allows for setting up differing expectations based on the arguments
provided to expected calls.
Argument matching with closures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Instead of providing a built-in matcher for each argument, we can provide a
closure that matches all passed arguments at once:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->withArgs(closure);
The given closure receives all the arguments passed in the call to the expected
method. In this way, this expectation only applies to method calls where passed
arguments make the closure evaluate to true:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')->withArgs(function ($arg) {
if ($arg % 2 == 0) {
return true;
}
return false;
});
$mock->foo(4); // matches the expectation
$mock->foo(3); // throws a NoMatchingExpectationException
Argument matching with some of given values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We can provide expected arguments that match passed arguments when mocked method
is called.
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->withSomeOfArgs(arg1, arg2, arg3, ...);
The given expected arguments order doesn't matter.
Check if expected values are included or not, but type should be matched:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')
->withSomeOfArgs(1, 2);
$mock->foo(1, 2, 3); // matches the expectation
$mock->foo(3, 2, 1); // matches the expectation (passed order doesn't matter)
$mock->foo('1', '2'); // throws a NoMatchingExpectationException (type should be matched)
$mock->foo(3); // throws a NoMatchingExpectationException
Any, or no arguments
^^^^^^^^^^^^^^^^^^^^
We can declare that the expectation matches a method call regardless of what
arguments are passed:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->withAnyArgs();
This is set by default unless otherwise specified.
We can declare that the expectation matches method calls with zero arguments:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->withNoArgs();
Declaring Return Value Expectations
-----------------------------------
For mock objects, we can tell Mockery what return values to return from the
expected method calls.
For that we can use the ``andReturn()`` method:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturn($value);
This sets a value to be returned from the expected method call.
It is possible to set up expectation for multiple return values. By providing
a sequence of return values, we tell Mockery what value to return on every
subsequent call to the method:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturn($value1, $value2, ...)
The first call will return ``$value1`` and the second call will return ``$value2``.
If we call the method more times than the number of return values we declared,
Mockery will return the final value for any subsequent method call:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')->andReturn(1, 2, 3);
$mock->foo(); // int(1)
$mock->foo(); // int(2)
$mock->foo(); // int(3)
$mock->foo(); // int(3)
The same can be achieved using the alternative syntax:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnValues([$value1, $value2, ...])
It accepts a simple array instead of a list of parameters. The order of return
is determined by the numerical index of the given array with the last array
member being returned on all calls once previous return values are exhausted.
The following two options are primarily for communication with test readers:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnNull();
// or
$mock->shouldReceive('name_of_method')
->andReturn([null]);
They mark the mock object method call as returning ``null`` or nothing.
Sometimes we want to calculate the return results of the method calls, based on
the arguments passed to the method. We can do that with the ``andReturnUsing()``
method which accepts one or more closure:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnUsing(closure, ...);
Closures can be queued by passing them as extra parameters as for ``andReturn()``.
Occasionally, it can be useful to echo back one of the arguments that a method
is called with. In this case we can use the ``andReturnArg()`` method; the
argument to be returned is specified by its index in the arguments list:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnArg(1);
This returns the second argument (index #1) from the list of arguments when the
method is called.
.. note::
We cannot currently mix ``andReturnUsing()`` or ``andReturnArg`` with
``andReturn()``.
If we are mocking fluid interfaces, the following method will be helpful:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnSelf();
It sets the return value to the mocked class name.
Throwing Exceptions
-------------------
We can tell the method of mock objects to throw exceptions:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andThrow(new Exception);
It will throw the given ``Exception`` object when called.
Rather than an object, we can pass in the ``Exception`` class, message and/or code to
use when throwing an ``Exception`` from the mocked method:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andThrow('exception_name', 'message', 123456789);
.. _expectations-setting-public-properties:
Setting Public Properties
-------------------------
Used with an expectation so that when a matching method is called, we can cause
a mock object's public property to be set to a specified value, by using
``andSet()`` or ``set()``:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andSet($property, $value);
// or
$mock->shouldReceive('name_of_method')
->set($property, $value);
In cases where we want to call the real method of the class that was mocked and
return its result, the ``passthru()`` method tells the expectation to bypass
a return queue:
.. code-block:: php
passthru()
It allows expectation matching and call count validation to be applied against
real methods while still calling the real class method with the expected
arguments.
Declaring Call Count Expectations
---------------------------------
Besides setting expectations on the arguments of the method calls, and the
return values of those same calls, we can set expectations on how many times
should any method be called.
When a call count expectation is not met, a
``\Mockery\Expectation\InvalidCountException`` will be thrown.
.. note::
It is absolutely required to call ``\Mockery::close()`` at the end of our
tests, for example in the ``tearDown()`` method of PHPUnit. Otherwise
Mockery will not verify the calls made against our mock objects.
We can declare that the expected method may be called zero or more times:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->zeroOrMoreTimes();
This is the default for all methods unless otherwise set.
To tell Mockery to expect an exact number of calls to a method, we can use the
following:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->times($n);
where ``$n`` is the number of times the method should be called.
A couple of most common cases got their shorthand methods.
To declare that the expected method must be called one time only:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->once();
To declare that the expected method must be called two times:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->twice();
To declare that the expected method must never be called:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->never();
Call count modifiers
^^^^^^^^^^^^^^^^^^^^
The call count expectations can have modifiers set.
If we want to tell Mockery the minimum number of times a method should be called,
we use ``atLeast()``:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->atLeast()
->times(3);
``atLeast()->times(3)`` means the call must be called at least three times
(given matching method args) but never less than three times.
Similarly, we can tell Mockery the maximum number of times a method should be
called, using ``atMost()``:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->atMost()
->times(3);
``atMost()->times(3)`` means the call must be called no more than three times.
If the method gets no calls at all, the expectation will still be met.
We can also set a range of call counts, using ``between()``:
.. code-block:: php
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->between($min, $max);
This is actually identical to using ``atLeast()->times($min)->atMost()->times($max)``
but is provided as a shorthand. It may be followed by a ``times()`` call with no
parameter to preserve the APIs natural language readability.
Multiple Calls with Different Expectations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If a method is expected to get called multiple times with different arguments
and/or return values we can simply repeat the expectations. The same of course
also works if we expect multiple calls to different methods.
.. code-block:: php
$mock = \Mockery::mock('MyClass');
// Expectations for the 1st call
$mock->shouldReceive('name_of_method')
->once()
->with('arg1')
->andReturn($value1)
// 2nd call to same method
->shouldReceive('name_of_method')
->once()
->with('arg2')
->andReturn($value2)
// final call to another method
->shouldReceive('other_method')
->once()
->with('other')
->andReturn($value_other);
Expectation Declaration Utilities
---------------------------------
Declares that this method is expected to be called in a specific order in
relation to similarly marked methods.
.. code-block:: php
ordered()
The order is dictated by the order in which this modifier is actually used when
setting up mocks.
Declares the method as belonging to an order group (which can be named or
numbered). Methods within a group can be called in any order, but the ordered
calls from outside the group are ordered in relation to the group:
.. code-block:: php
ordered(group)
We can set up so that method1 is called before group1 which is in turn called
before method2.
When called prior to ``ordered()`` or ``ordered(group)``, it declares this
ordering to apply across all mock objects (not just the current mock):
.. code-block:: php
globally()
This allows for dictating order expectations across multiple mocks.
The ``byDefault()`` marks an expectation as a default. Default expectations are
applied unless a non-default expectation is created:
.. code-block:: php
byDefault()
These later expectations immediately replace the previously defined default.
This is useful so we can setup default mocks in our unit test ``setup()`` and
later tweak them in specific tests as needed.
Returns the current mock object from an expectation chain:
.. code-block:: php
getMock()
Useful where we prefer to keep mock setups as a single statement, e.g.:
.. code-block:: php
$mock = \Mockery::mock('foo')->shouldReceive('foo')->andReturn(1)->getMock();

View File

@@ -0,0 +1,29 @@
.. index::
single: Mocking; Final Classes/Methods
Dealing with Final Classes/Methods
==================================
One of the primary restrictions of mock objects in PHP, is that mocking
classes or methods marked final is hard. The final keyword prevents methods so
marked from being replaced in subclasses (subclassing is how mock objects can
inherit the type of the class or object being mocked).
The simplest solution is to implement an interface in your final class and
typehint against / mock this.
However this may not be possible in some third party libraries.
Mockery does allow creating "proxy mocks" from classes marked final, or from
classes with methods marked final. This offers all the usual mock object
goodness but the resulting mock will not inherit the class type of the object
being mocked, i.e. it will not pass any instanceof comparison. Methods marked
as final will be proxied to the original method, i.e., final methods can't be
mocked.
We can create a proxy mock by passing the instantiated object we wish to
mock into ``\Mockery::mock()``, i.e. Mockery will then generate a Proxy to the
real object and selectively intercept method calls for the purposes of setting
and meeting expectations.
See the :ref:`creating-test-doubles-partial-test-doubles` chapter, the subsection
about proxied partial test doubles.

View File

@@ -0,0 +1,23 @@
Reference
=========
.. toctree::
:hidden:
creating_test_doubles
expectations
argument_validation
alternative_should_receive_syntax
spies
instance_mocking
partial_mocks
protected_methods
public_properties
public_static_properties
pass_by_reference_behaviours
demeter_chains
final_methods_classes
magic_methods
phpunit_integration
.. include:: map.rst.inc

View File

@@ -0,0 +1,22 @@
.. index::
single: Mocking; Instance
Instance Mocking
================
Instance mocking means that a statement like:
.. code-block:: php
$obj = new \MyNamespace\Foo;
...will actually generate a mock object. This is done by replacing the real
class with an instance mock (similar to an alias mock), as with mocking public
methods. The alias will import its expectations from the original mock of
that type (note that the original is never verified and should be ignored
after its expectations are setup). This lets you intercept instantiation where
you can't simply inject a replacement object.
As before, this does not prevent a require statement from including the real
class and triggering a fatal PHP error. It's intended for use where
autoloading is the primary class loading mechanism.

View File

@@ -0,0 +1,16 @@
.. index::
single: Mocking; Magic Methods
PHP Magic Methods
=================
PHP magic methods which are prefixed with a double underscore, e.g.
``__set()``, pose a particular problem in mocking and unit testing in general.
It is strongly recommended that unit tests and mock objects do not directly
refer to magic methods. Instead, refer only to the virtual methods and
properties these magic methods simulate.
Following this piece of advice will ensure we are testing the real API of
classes and also ensures there is no conflict should Mockery override these
magic methods, which it will inevitably do in order to support its role in
intercepting method calls and properties.

View File

@@ -0,0 +1,14 @@
* :doc:`/reference/creating_test_doubles`
* :doc:`/reference/expectations`
* :doc:`/reference/argument_validation`
* :doc:`/reference/alternative_should_receive_syntax`
* :doc:`/reference/spies`
* :doc:`/reference/partial_mocks`
* :doc:`/reference/protected_methods`
* :doc:`/reference/public_properties`
* :doc:`/reference/public_static_properties`
* :doc:`/reference/pass_by_reference_behaviours`
* :doc:`/reference/demeter_chains`
* :doc:`/reference/final_methods_classes`
* :doc:`/reference/magic_methods`
* :doc:`/reference/phpunit_integration`

View File

@@ -0,0 +1,108 @@
.. index::
single: Mocking; Partial Mocks
Creating Partial Mocks
======================
Partial mocks are useful when we only need to mock several methods of an
object leaving the remainder free to respond to calls normally (i.e. as
implemented). Mockery implements three distinct strategies for creating
partials. Each has specific advantages and disadvantages so which strategy we
use will depend on our own preferences and the source code in need of
mocking.
We have previously talked a bit about :ref:`creating-test-doubles-partial-test-doubles`,
but we'd like to expand on the subject a bit here.
#. Runtime partial test doubles
#. Generated partial test doubles
#. Proxied Partial Mock
Runtime partial test doubles
----------------------------
A runtime partial test double, also known as a passive partial mock, is a kind
of a default state of being for a mocked object.
.. code-block:: php
$mock = \Mockery::mock('MyClass')->makePartial();
With a runtime partial, we assume that all methods will simply defer to the
parent class (``MyClass``) original methods unless a method call matches a
known expectation. If we have no matching expectation for a specific method
call, that call is deferred to the class being mocked. Since the division
between mocked and unmocked calls depends entirely on the expectations we
define, there is no need to define which methods to mock in advance.
See the cookbook entry on :doc:`../cookbook/big_parent_class` for an example
usage of runtime partial test doubles.
Generated Partial Test Doubles
------------------------------
A generated partial test double, also known as a traditional partial mock,
defines ahead of time which methods of a class are to be mocked and which are
to be left unmocked (i.e. callable as normal). The syntax for creating
traditional mocks is:
.. code-block:: php
$mock = \Mockery::mock('MyClass[foo,bar]');
In the above example, the ``foo()`` and ``bar()`` methods of MyClass will be
mocked but no other MyClass methods are touched. We will need to define
expectations for the ``foo()`` and ``bar()`` methods to dictate their mocked
behaviour.
Don't forget that we can pass in constructor arguments since unmocked methods
may rely on those!
.. code-block:: php
$mock = \Mockery::mock('MyNamespace\MyClass[foo]', array($arg1, $arg2));
See the :ref:`creating-test-doubles-constructor-arguments` section to read up
on them.
.. note::
Even though we support generated partial test doubles, we do not recommend
using them.
Proxied Partial Mock
--------------------
A proxied partial mock is a partial of last resort. We may encounter a class
which is simply not capable of being mocked because it has been marked as
final. Similarly, we may find a class with methods marked as final. In such a
scenario, we cannot simply extend the class and override methods to mock - we
need to get creative.
.. code-block:: php
$mock = \Mockery::mock(new MyClass);
Yes, the new mock is a Proxy. It intercepts calls and reroutes them to the
proxied object (which we construct and pass in) for methods which are not
subject to any expectations. Indirectly, this allows us to mock methods
marked final since the Proxy is not subject to those limitations. The tradeoff
should be obvious - a proxied partial will fail any typehint checks for the
class being mocked since it cannot extend that class.
Special Internal Cases
----------------------
All mock objects, with the exception of Proxied Partials, allows us to make
any expectation call to the underlying real class method using the ``passthru()``
expectation call. This will return values from the real call and bypass any
mocked return queue (which can simply be omitted obviously).
There is a fourth kind of partial mock reserved for internal use. This is
automatically generated when we attempt to mock a class containing methods
marked final. Since we cannot override such methods, they are simply left
unmocked. Typically, we don't need to worry about this but if we really
really must mock a final method, the only possible means is through a Proxied
Partial Mock. SplFileInfo, for example, is a common class subject to this form
of automatic internal partial since it contains public final methods used
internally.

View File

@@ -0,0 +1,130 @@
.. index::
single: Pass-By-Reference Method Parameter Behaviour
Preserving Pass-By-Reference Method Parameter Behaviour
=======================================================
PHP Class method may accept parameters by reference. In this case, changes
made to the parameter (a reference to the original variable passed to the
method) are reflected in the original variable. An example:
.. code-block:: php
class Foo
{
public function bar(&$a)
{
$a++;
}
}
$baz = 1;
$foo = new Foo;
$foo->bar($baz);
echo $baz; // will echo the integer 2
In the example above, the variable ``$baz`` is passed by reference to
``Foo::bar()`` (notice the ``&`` symbol in front of the parameter?). Any
change ``bar()`` makes to the parameter reference is reflected in the original
variable, ``$baz``.
Mockery handles references correctly for all methods where it can analyse
the parameter (using ``Reflection``) to see if it is passed by reference. To
mock how a reference is manipulated by the class method, we can use a closure
argument matcher to manipulate it, i.e. ``\Mockery::on()`` - see the
:ref:`argument-validation-complex-argument-validation` chapter.
There is an exception for internal PHP classes where Mockery cannot analyse
method parameters using ``Reflection`` (a limitation in PHP). To work around
this, we can explicitly declare method parameters for an internal class using
``\Mockery\Configuration::setInternalClassMethodParamMap()``.
Here's an example using ``MongoCollection::insert()``. ``MongoCollection`` is
an internal class offered by the mongo extension from PECL. Its ``insert()``
method accepts an array of data as the first parameter, and an optional
options array as the second parameter. The original data array is updated
(i.e. when a ``insert()`` pass-by-reference parameter) to include a new
``_id`` field. We can mock this behaviour using a configured parameter map (to
tell Mockery to expect a pass by reference parameter) and a ``Closure``
attached to the expected method parameter to be updated.
Here's a PHPUnit unit test verifying that this pass-by-reference behaviour is
preserved:
.. code-block:: php
public function testCanOverrideExpectedParametersOfInternalPHPClassesToPreserveRefs()
{
\Mockery::getConfiguration()->setInternalClassMethodParamMap(
'MongoCollection',
'insert',
array('&$data', '$options = array()')
);
$m = \Mockery::mock('MongoCollection');
$m->shouldReceive('insert')->with(
\Mockery::on(function(&$data) {
if (!is_array($data)) return false;
$data['_id'] = 123;
return true;
}),
\Mockery::any()
);
$data = array('a'=>1,'b'=>2);
$m->insert($data);
$this->assertTrue(isset($data['_id']));
$this->assertEquals(123, $data['_id']);
\Mockery::resetContainer();
}
Protected Methods
-----------------
When dealing with protected methods, and trying to preserve pass by reference
behavior for them, a different approach is required.
.. code-block:: php
class Model
{
public function test(&$data)
{
return $this->doTest($data);
}
protected function doTest(&$data)
{
$data['something'] = 'wrong';
return $this;
}
}
class Test extends \PHPUnit\Framework\TestCase
{
public function testModel()
{
$mock = \Mockery::mock('Model[test]')->shouldAllowMockingProtectedMethods();
$mock->shouldReceive('test')
->with(\Mockery::on(function(&$data) {
$data['something'] = 'wrong';
return true;
}));
$data = array('foo' => 'bar');
$mock->test($data);
$this->assertTrue(isset($data['something']));
$this->assertEquals('wrong', $data['something']);
}
}
This is quite an edge case, so we need to change the original code a little bit,
by creating a public method that will call our protected method, and then mock
that, instead of the protected method. This new public method will act as a
proxy to our protected method.

View File

@@ -0,0 +1,145 @@
.. index::
single: PHPUnit Integration
PHPUnit Integration
===================
Mockery was designed as a simple-to-use *standalone* mock object framework, so
its need for integration with any testing framework is entirely optional. To
integrate Mockery, we need to define a ``tearDown()`` method for our tests
containing the following (we may use a shorter ``\Mockery`` namespace
alias):
.. code-block:: php
public function tearDown() {
\Mockery::close();
}
This static call cleans up the Mockery container used by the current test, and
run any verification tasks needed for our expectations.
For some added brevity when it comes to using Mockery, we can also explicitly
use the Mockery namespace with a shorter alias. For example:
.. code-block:: php
use \Mockery as m;
class SimpleTest extends \PHPUnit\Framework\TestCase
{
public function testSimpleMock() {
$mock = m::mock('simplemock');
$mock->shouldReceive('foo')->with(5, m::any())->once()->andReturn(10);
$this->assertEquals(10, $mock->foo(5));
}
public function tearDown() {
m::close();
}
}
Mockery ships with an autoloader so we don't need to litter our tests with
``require_once()`` calls. To use it, ensure Mockery is on our
``include_path`` and add the following to our test suite's ``Bootstrap.php``
or ``TestHelper.php`` file:
.. code-block:: php
require_once 'Mockery/Loader.php';
require_once 'Hamcrest/Hamcrest.php';
$loader = new \Mockery\Loader;
$loader->register();
If we are using Composer, we can simplify this to including the Composer
generated autoloader file:
.. code-block:: php
require __DIR__ . '/../vendor/autoload.php'; // assuming vendor is one directory up
.. caution::
Prior to Hamcrest 1.0.0, the ``Hamcrest.php`` file name had a small "h"
(i.e. ``hamcrest.php``). If upgrading Hamcrest to 1.0.0 remember to check
the file name is updated for all your projects.)
To integrate Mockery into PHPUnit and avoid having to call the close method
and have Mockery remove itself from code coverage reports, have your test case
extends the ``\Mockery\Adapter\Phpunit\MockeryTestCase``:
.. code-block:: php
class MyTest extends \Mockery\Adapter\Phpunit\MockeryTestCase
{
}
An alternative is to use the supplied trait:
.. code-block:: php
class MyTest extends \PHPUnit\Framework\TestCase
{
use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
}
Extending ``MockeryTestCase`` or using the ``MockeryPHPUnitIntegration``
trait is **the recommended way** of integrating Mockery with PHPUnit,
since Mockery 1.0.0.
PHPUnit listener
----------------
Before the 1.0.0 release, Mockery provided a PHPUnit listener that would
call ``Mockery::close()`` for us at the end of a test. This has changed
significantly since the 1.0.0 version.
Now, Mockery provides a PHPUnit listener that makes tests fail if
``Mockery::close()`` has not been called. It can help identify tests where
we've forgotten to include the trait or extend the ``MockeryTestCase``.
If we are using PHPUnit's XML configuration approach, we can include the
following to load the ``TestListener``:
.. code-block:: xml
<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener"></listener>
</listeners>
Make sure Composer's or Mockery's autoloader is present in the bootstrap file
or we will need to also define a "file" attribute pointing to the file of the
``TestListener`` class.
If we are creating the test suite programmatically we may add the listener
like this:
.. code-block:: php
// Create the suite.
$suite = new PHPUnit\Framework\TestSuite();
// Create the listener and add it to the suite.
$result = new PHPUnit\Framework\TestResult();
$result->addListener(new \Mockery\Adapter\Phpunit\TestListener());
// Run the tests.
$suite->run($result);
.. caution::
PHPUnit provides a functionality that allows
`tests to run in a separated process <http://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.runTestsInSeparateProcesses>`_,
to ensure better isolation. Mockery verifies the mocks expectations using the
``Mockery::close()`` method, and provides a PHPUnit listener, that automatically
calls this method for us after every test.
However, this listener is not called in the right process when using
PHPUnit's process isolation, resulting in expectations that might not be
respected, but without raising any ``Mockery\Exception``. To avoid this,
we cannot rely on the supplied Mockery PHPUnit ``TestListener``, and we need
to explicitly call ``Mockery::close``. The easiest solution to include this
call in the ``tearDown()`` method, as explained previously.

View File

@@ -0,0 +1,26 @@
.. index::
single: Mocking; Protected Methods
Mocking Protected Methods
=========================
By default, Mockery does not allow mocking protected methods. We do not recommend
mocking protected methods, but there are cases when there is no other solution.
For those cases we have the ``shouldAllowMockingProtectedMethods()`` method. It
instructs Mockery to specifically allow mocking of protected methods, for that
one class only:
.. code-block:: php
class MyClass
{
protected function foo()
{
}
}
$mock = \Mockery::mock('MyClass')
->shouldAllowMockingProtectedMethods();
$mock->shouldReceive('foo');

View File

@@ -0,0 +1,20 @@
.. index::
single: Mocking; Public Properties
Mocking Public Properties
=========================
Mockery allows us to mock properties in several ways. One way is that we can set
a public property and its value on any mock object. The second is that we can
use the expectation methods ``set()`` and ``andSet()`` to set property values if
that expectation is ever met.
You can read more about :ref:`expectations-setting-public-properties`.
.. note::
In general, Mockery does not support mocking any magic methods since these
are generally not considered a public API (and besides it is a bit difficult
to differentiate them when you badly need them for mocking!). So please mock
virtual properties (those relying on ``__get()`` and ``__set()``) as if they
were actually declared on the class.

View File

@@ -0,0 +1,15 @@
.. index::
single: Mocking; Public Static Methods
Mocking Public Static Methods
=============================
Static methods are not called on real objects, so normal mock objects can't
mock them. Mockery supports class aliased mocks, mocks representing a class
name which would normally be loaded (via autoloading or a require statement)
in the system under test. These aliases block that loading (unless via a
require statement - so please use autoloading!) and allow Mockery to intercept
static method calls and add expectations for them.
See the :ref:`creating-test-doubles-aliasing` section for more information on
creating aliased mocks, for the purpose of mocking public static methods.

View File

@@ -0,0 +1,154 @@
.. index::
single: Reference; Spies
Spies
=====
Spies are a type of test doubles, but they differ from stubs or mocks in that,
that the spies record any interaction between the spy and the System Under Test
(SUT), and allow us to make assertions against those interactions after the fact.
Creating a spy means we don't have to set up expectations for every method call
the double might receive during the test, some of which may not be relevant to
the current test. A spy allows us to make assertions about the calls we care
about for this test only, reducing the chances of over-specification and making
our tests more clear.
Spies also allow us to follow the more familiar Arrange-Act-Assert or
Given-When-Then style within our tests. With mocks, we have to follow a less
familiar style, something along the lines of Arrange-Expect-Act-Assert, where
we have to tell our mocks what to expect before we act on the SUT, then assert
that those expectations were met:
.. code-block:: php
// arrange
$mock = \Mockery::mock('MyDependency');
$sut = new MyClass($mock);
// expect
$mock->shouldReceive('foo')
->once()
->with('bar');
// act
$sut->callFoo();
// assert
\Mockery::close();
Spies allow us to skip the expect part and move the assertion to after we have
acted on the SUT, usually making our tests more readable:
.. code-block:: php
// arrange
$spy = \Mockery::spy('MyDependency');
$sut = new MyClass($spy);
// act
$sut->callFoo();
// assert
$spy->shouldHaveReceived()
->foo()
->with('bar');
On the other hand, spies are far less restrictive than mocks, meaning tests are
usually less precise, as they let us get away with more. This is usually a
good thing, they should only be as precise as they need to be, but while spies
make our tests more intent-revealing, they do tend to reveal less about the
design of the SUT. If we're having to setup lots of expectations for a mock,
in lots of different tests, our tests are trying to tell us something - the SUT
is doing too much and probably should be refactored. We don't get this with
spies, they simply ignore the calls that aren't relevant to them.
Another downside to using spies is debugging. When a mock receives a call that
it wasn't expecting, it immediately throws an exception (failing fast), giving
us a nice stack trace or possibly even invoking our debugger. With spies, we're
simply asserting calls were made after the fact, so if the wrong calls were made,
we don't have quite the same just in time context we have with the mocks.
Finally, if we need to define a return value for our test double, we can't do
that with a spy, only with a mock object.
.. note::
This documentation page is an adaption of the blog post titled
`"Mockery Spies" <https://davedevelopment.co.uk/2014/10/09/mockery-spies.html>`_,
published by Dave Marshall on his blog. Dave is the original author of spies
in Mockery.
Spies Reference
---------------
To verify that a method was called on a spy, we use the ``shouldHaveReceived()``
method:
.. code-block:: php
$spy->shouldHaveReceived('foo');
To verify that a method was **not** called on a spy, we use the
``shouldNotHaveReceived()`` method:
.. code-block:: php
$spy->shouldNotHaveReceived('foo');
We can also do argument matching with spies:
.. code-block:: php
$spy->shouldHaveReceived('foo')
->with('bar');
Argument matching is also possible by passing in an array of arguments to
match:
.. code-block:: php
$spy->shouldHaveReceived('foo', ['bar']);
Although when verifying a method was not called, the argument matching can only
be done by supplying the array of arguments as the 2nd argument to the
``shouldNotHaveReceived()`` method:
.. code-block:: php
$spy->shouldNotHaveReceived('foo', ['bar']);
This is due to Mockery's internals.
Finally, when expecting calls that should have been received, we can also verify
the number of calls:
.. code-block:: php
$spy->shouldHaveReceived('foo')
->with('bar')
->twice();
Alternative shouldReceive syntax
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As of Mockery 1.0.0, we support calling methods as we would call any PHP method,
and not as string arguments to Mockery ``should*`` methods.
In cases of spies, this only applies to the ``shouldHaveReceived()`` method:
.. code-block:: php
$spy->shouldHaveReceived()
->foo('bar');
We can set expectation on number of calls as well:
.. code-block:: php
$spy->shouldHaveReceived()
->foo('bar')
->twice();
Unfortunately, due to limitations we can't support the same syntax for the
``shouldNotHaveReceived()`` method.