Creating Mocks ¶
The Phake::mock()
method is how you create new test doubles in Phake. You pass in the class name of what you would
like to mock.
$mock = Phake::mock(ClassToMock::class);
The $mock
variable is now an instance of a generated class that inherits from ClassToMock
with hooks that allow
you to force functions to return known values. By default, all methods on a mock object will return the type specified
in the return type or null. This behavior can be overridden on a per method and even per parameter basis. This will be
covered in depth in Method Stubbing.
The mock will also record all calls made to this class so that you can later verify that specific methods were called with the proper parameters. This will be covered in depth in Method Verification.
In addition to classes you can also mock interfaces directly. This is done in much the same way as a class name, you
simply pass the interface name as the first parameter to Phake::mock()
.
$mock = Phake::mock(InterfaceToMock::class);
You can also pass an array of interface names to Phake::mock()
that also contains up to 1 class name. This allows
for easier mocking of a dependency that is required to implement multiple interfaces.
$mock = Phake::mock([ Interface1::class, Interface2::class ]);
Partial Mocks ¶
When testing legacy code, you may find that a better default behavior for the methods is to actually call the original
method. This can be accomplished by stubbing each of the methods to return thenCallParent()
. You can learn more
about this in Calling the Parent.
While this is certainly possible, you may find it easier to just use a partial mock in Phake. Phake partial mocks also
allow you to call the actual constructor of the class being mocked. They are created using Phake::partialMock()
.
Like Phake::mock()
, the first parameter is the name of the class that you are mocking. However, you can pass
additional parameters that will then be passed as the respective parameters to that class’ constructor. The other
notable feature of a partial mock in Phake is that its default answer is to pass the call through to the parent as if
you were using thenCallParent()
.
Consider the following class that has a method that simply returns the value passed into the constructor.
class MyClass
{
private $value;
public __construct($value)
{
$this->value = $value;
}
public function foo()
{
return $this->value;
}
}
Using Phake::partialMock()
you can instantiate a mock object that will allow this object to function
as designed while still allowing verification as well as selective stubbing of certain calls.
Below is an example that shows the usage of Phake::partialMock()
.
class MyClassTest extends PHPUnit\Framework\TestCase
{
public function testCallingParent()
{
$mock = Phake::partialMock(MyClass::class, 42);
$this->assertEquals(42, $mock->foo());
}
}
Again, partial mocks should not be used when you are testing new code. If you find yourself using them be sure to inspect your design to make sure that the class you are creating a partial mock for is not doing too much.
Calling Private and Protected Methods on Mocks ¶
It is possible to invoke protected and private methods on your mocks using Phake. When you mock
a class, the mocked version will retain the same visibility on each of its functions as you would have had on your
original class. However, using Phake::makeVisible()
and Phake::makeStaticsVisible()
you can allow direct
invocation of instance methods and static methods accordingly. Both of these methods accept a mock object as its only
parameter and returns a proxy class that you can invoke the methods on. Method calls on these proxies will still
return whatever value was previously stubbed for that method call. So if you intend on the original method being called
and you aren't using Partial Mocks, then you can just enable calling the parent for that method call using
the thenCallParent()
answer. This is all discussed in greater depth in Method Stubbing and Answers.
class MyClass
{
private function foo()
{
}
private static function bar()
{
}
}
Given the class above, you can invoke both private methods with the code below.
$mock = Phake::mock(MyClass::class);
Phake::makeVisible($mock)->foo();
Phake::makeStaticsVisible($mock)->bar();
As you can see above when using the static variant you still call the method as though it were an instance method. The
other thing to take note of is that there is no modification done on $mock itself. If you use Phake::makeVisible()
you will only be able to make those private and protected calls off of the return of that method itself.
The best use case for this feature of Phake is if you have private or protected calls that are nested deep inside of
public methods. Generally speaking you would always just test from your class's public interface. However these large
legacy classes often require a significant amount of setup within fixtures to allow for calling those private and
protected methods. If you are only intending on refactoring the private and protected method then using
Phake::makeVisible()
removes the need for these complex fixtures.
Consider this really poor object oriented code. The cleanRowContent()
function does some basic text processing such as
stripping html tags, cleaning up links, etc. It turns out that the original version of this method is written in a very
unperformant manner and I have been tasked with rewriting it.
class MyReallyTerribleOldClass
{
public function __construct(Database $db)
{
//...
}
public function doWayTooMuch($data)
{
$result = $this->db->query($this->getQueryForData($data))
$rows = array();
while ($row = $this->db->fetch($result))
{
$rows[] = $this->cleanRowContent($row);
}
return $rows;
}
private function cleanRowContent($row)
{
//...
}
private function getQueryForData($data)
{
//...
}
}
If I was about to make changes to cleanRowContent
and wanted to make sure I didn't break previous functionality, in order to
do so with the traditional fixture I would have to write a test similar to the following:
class Test extends PHPUnit\Framework\TestCase
{
public function testProcessRow()
{
$dbRow = [ 'id' => '1', 'content' => 'Text to be processed with <b>tags stripped</b>' ];
$expectedValue = [ [ 'id' => 1', 'content' => 'Text to be processed with tags stripped' ] ];
$db = Phake::mock(Database::class);
$result = Phake::mock(DatabaseResult::class);
$oldClass = new MyReallyTerribleOldClass($db);
Phake::when($db)->query->thenReturn($result);
Phake::when($db)->fetch->thenReturn($dbRow)->thenReturn(null);
$data = $oldClass->doWayTooMuch(array());
$this->assertEquals($expectedValue, $data);
}
}
Using test helpers or PHPUnit data providers I could reuse this test to make sure I fully cover the various logic paths
and use cases for the cleanRowContent()
. However this test is doing alot of work to just set up this scenario. Whenever
your test is hitting code not relevant to your test in increases the test's fragility. Here is how you could test the
same code using Phake::makeVisible()
.
class Test extends PHPUnit\Framework\TestCase
{
public function testProcessRow()
{
$dbRow = [ 'id' => '1', 'content' => 'Text to be processed with <b>tags stripped</b>' ];
$expectedValue = [ [ 'id' => 1', 'content' => 'Text to be processed with tags stripped' ] ];
$oldClass = new Phake::partialMock(MyReallyTerribleOldClass::class);
$data = Phake::makeVisible($oldClass)->cleanRowContent($dbRow);
$this->assertEquals($expectedValue, $data);
}
}
As you can see the test is significantly simpler. One final note, if you find yourself using this strategy on newly
written code, it could be a code smell indicitive of a class or public method doing too much. It is very reasonable
to argue that in my example, the cleanRowContent()
method should be a class in and of itself or possibly a method
on a string manipulation type of class that my class then calls out to. This is a better design and also a much easier
to test design.