Method Verification ¶
The Phake::verify()
method is used to assert that method calls have been
made on a mock object that you can create with Phake::mock()
.
Phake::verify()
accepts the mock object you want to verify calls against.
Mock objects in Phake can almost be viewed as a tape recorder. Any time the code you are testing
calls a method on an object you create with Phake::mock()
it is going to
record the method that you called along with all of the parameters used to call that method. Then
Phake::verify()
will look at that recording and allow you to assert whether
or not a certain call was made.
class PhakeTest1 extends PHPUnit\Framework\TestCase
{
public function testBasicVerify()
{
$mock = Phake::mock(MyClass::class);
$mock->foo();
Phake::verify($mock)->foo();
}
}
The Phake::verify()
call here, verifies that the method foo()
has been called once (and only once) with no
parameters on the object $mock
. A very important thing to note here that is a departure from most (if not all)
other PHP mocking frameworks is that you want to verify the method call AFTER the method call takes place. Other
mocking frameworks such as the one built into PHPUnit depend on you setting the expectations of what will get called
prior to running the system under test.
Phake strives to allow you to follow the four phases of a unit test as laid out in xUnit Test Patterns: setup,
exercise, verify, and teardown. The setup phase of a test using Phake for mocking will now include calls to
Phake::mock()
for each class you want to mock. The exercise portion of your code will remain the same. The verify
section of your code will include calls to Phake::verify()
. The exercise and teardown phases will remain unchanged.
Verifying Method Parameters ¶
Verifying method parameters using Phake is very simple yet can be very flexible. There are a wealth of options for matching parameters that are discussed later on in Method Parameter Matchers.
Verifying Multiple Invocations ¶
A common need for mock objects is the ability to have variable multiple invocations on that object. Phake allows you to
use Phake::verify()
multiple times on the same object. A notable difference between Phake and PHPUnit’s mocking
framework is the ability to mock multiple invocations of the same method with no regard for call sequences. The PHPUnit
mocking test below would fail for this reason.
class MyTest extends PHPUnit\Framework\TestCase
{
public function testPHPUnitMock()
{
$mock = $this->getMock(PhakeTest_MockedClass::class);
$mock->expects($this->once())->method('fooWithArgument')
->with('foo');
$mock->expects($this->once())->method('fooWithArgument')
->with('bar');
$mock->fooWithArgument('foo');
$mock->fooWithArgument('bar');
}
}
The reason this test fails is because by default PHPUnit only allows a single expectation per method. The way you can
fix this is by using the at()
matcher. This allows you to specify the index of the invocation you want to match
again. So to make the test above work you would have to change it.
class MyTest extends PHPUnit\Framework\TestCase
{
public function testPHPUnitMock()
{
$mock = $this->getMock(PhakeTest_MockedClass::class);
//NOTICE this is now at() instead of once()
$mock->expects($this->at(0))->method('fooWithArgument')
->with('foo');
//NOTICE this is now at() instead of once()
$mock->expects($this->at(1))->method('fooWithArgument')
->with('bar');
$mock->fooWithArgument('foo');
$mock->fooWithArgument('bar');
}
}
This test will now run as expected. There is still one small problem however and that is that you are now testing not just the invocations but also the order of invocations. Many times the order in which two calls are made really do not matter. If swapping the order of two method calls will not break your application then there is no reason to enforce that code structure through a unit test. Unfortunately, you cannot have multiple invocations of a method in PHPUnit without enforcing call order. In Phake these two notions of call order and multiple invocations are kept completely distinct. Here is the same test written using Phake.
class MyTest extends PHPUnit\Framework\TestCase
{
public function testPHPUnitMock()
{
$mock = Phake::mock(PhakeTest_MockedClass::class);
$mock->fooWithArgument('foo');
$mock->fooWithArgument('bar');
Phake::verify($mock)->fooWithArgument('foo');
Phake::verify($mock)->fooWithArgument('bar');
}
}
You can switch the calls around in this example as much as you like and the test will still pass. You can mock as many different invocations of the same method as you need.
If you would like to verify the exact same parameters are used on a method multiple times (or they all match the same
constraints multiple times) then you can use the verification mode parameter of Phake::verify()
. The second
parameter to Phake::verify()
allows you to specify how many times you expect that method to be called with matching
parameters. If no value is specified then the default of one is used. The other options are:
Phake::times($n)
– Where$n
equals the exact number of times you expect the method to be called.Phake::atLeast($n)
– Where$n
is the minimum number of times you expect the method to be called.Phake::atMost($n)
– Where$n
is the most number of times you would expect the method to be called.Phake::never()
- Same as callingPhake::times(0)
.
Here is an example of this in action.
class MyTest extends PHPUnit\Framework\TestCase
{
public function testPHPUnitMock()
{
$mock = Phake::mock(PhakeTest_MockedClass::class);
$mock->fooWithArgument('foo');
$mock->fooWithArgument('foo');
Phake::verify($mock, Phake::times(2))->fooWithArgument('foo');
}
}
Verifying Calls Happen in a Particular Order ¶
Sometimes the desired behavior is that you verify calls happen in a particular order. Say there is a functional reason
for the two variants of fooWithArgument()
to be called in the order of the original test. You can utilize
Phake::inOrder()
to ensure the order of your call invocations. Phake::inOrder()
takes one or more arguments and
errors out in the event that one of the verified calls was invoked out of order. The calls don’t have to be in exact
sequential order, there can be other calls in between, it just ensures the specified calls themselves are called in
order relative to each other. Below is an example Phake test that behaves similarly to the PHPUnit test that utilized
at()
.
class MyTest extends PHPUnit\Framework\TestCase
{
public function testPHPUnitMock()
{
$mock = Phake::mock(PhakeTest_MockedClass::class);
$mock->fooWithArgument('foo');
$mock->fooWithArgument('bar');
Phake::inOrder(
Phake::verify($mock)->fooWithArgument('foo'),
Phake::verify($mock)->fooWithArgument('bar')
);
}
}
Verifying No Interaction with a Mock so Far ¶
Occasionally you may want to ensure that no interactions have occurred with a mock object. This can be done
by passing your mock object to Phake::verifyNoInteraction($mock)
. This will not prevent further interaction
with your mock, it will simply tell you whether or not any interaction up to that point has happened. You
can pass multiple arguments to this method to verify no interaction with multiple mock objects.
Verifying No Further Interaction with a Mock ¶
There is a similar method to prevent any future interaction with a mock. This can be done by passing a mock
object to Phake::verifyNoFurtherInteraction($mock)
. You can pass multiple arguments to this method to
verify no further interaction occurs with multiple mock objects.
Verifying No Unverified Interaction with a Mock ¶
By default any unverified calls to a mock are ignored. That is to say, if a call is made to $mock->foo()
but
Phake::verify($mock)->foo()
is never used, then no failures are thrown. If you want to be stricter and ensure that
all calls have been verified you can call Phake::verifyNoOtherInteractions($mock)
at the end of your test. This will
check and make sure that all calls to your mock have been verified by one or more calls to Phake verify. This method
should only be used in those cases where you can clearly say that it is important that your test knows about all calls
on a particular object. One useful case for instance could be in testing a method that returns a filtered array.
class FilterTest {
public function testFilteredList()
{
$filter = new MyFilter();
$list = Phake::Mock(MyList::class);
$filter->addEvenToList([ 1, 2, 3, 4, 5 ], $list);
Phake::verify($list)->push(2);
Phake::verify($list)->push(4);
Phake::verifyNoOtherInteractions($list);
}
}
Without Phake::verifyNoOtherInteractions($list)
you would have to add additional verifications that $list->push()
was not called for the odd values in the list. This method should be used only when necessary. Using it in every test
is an anti-pattern that will lead to brittle tests.
Verifying Magic Methods ¶
Most magic methods can be verified using the method name just like you would any other method. The one exception to this
is the __call()
method. This method is overwritten on each mock already to allow for the fluent api that Phake
utilizes. If you want to verify a particular invocation of __call()
you can verify the actual method call by
mocking the method passed in as the first parameter.
Consider the following class.
class MagicClass
{
public function __call($method, $args)
{
return '__call';
}
}
You could mock an invocation of the __call()
method through a userspace call to magicCall()
with the following code.
class MagicClassTest extends PHPUnit\Framework\TestCase
{
public function testMagicCall()
{
$mock = Phake::mock(MagicClass::class);
$mock->magicCall();
Phake::verify($mock)->magicCall();
}
}
If for any reason you need to explicitly verify calls to __call()
then you can use Phake::verifyCallMethodWith()
.
class MagicClassTest extends PHPUnit\Framework\TestCase
{
public function testMagicCall()
{
$mock = Phake::mock(MagicClass::class);
$mock->magicCall(42);
Phake::verifyCallMethodWith('magicCall', [ 42 ])->isCalledOn($mock);
}
}