Agile Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

A Mockery review

05.05.2011
| 9130 views |
  • submit to reddit

Mockery is a mock object framework (more properly Test Double framework) from @padraicb, independent from testing frameworks like PHPUnit. It can be used to quickly prepare Mocks, Stubs and other Test Doubles to use inside your unit tests.

I've tried Mockery via a PEAR installation and I must say its expressive power is higher than that of PHPUnit mocking system. However, it may be too powerful for effective usage.

A first glance

Documentation for Mockery is self contained in its README file. Official documentation is not superb also for PHPUnit, but there are lots of unofficial guides (Practical series included) focused on PHPUnit as a standard.

However Mockery does not substitute PHPUnit completely, as it's only a mocking framework; if generating mocks, stubs and other test doubles with PHPUnit is not adequate for you, adopting a more powerful mocking mechanism than the bundled one is the natural choice.

Mockery has no dependencies other than the strongly suggested Hamcrest, also installable via PEAR. This version of Hamcrest is a port of the Hamcrest matchers for the PHP language. Mockery Api is mostly static, but also PHPUnit is under the covers. I didn't encounter issues deriving from this procedural approach.

Unique features

Mockery can produce Test Doubles for concrete classes, abstract classes or interfaces. In particular, it has some features which are not found in PHPUnit and that makes it interesting:

  • alternative expectations which apply only when the particular set of params is used. This allows for clever mock objects which return different results in different calls.
  • recording of expected calls to a mock instead of defining them via the Api.
  • mocking of not existent methods, which is on the border line of potentially harmful features. However, it may serve to develop with a mock before extracting an interface. An interface which does not exists has not to be kept in sync with the tests during these troublesome initial stages, but only created before the next commit.

Another point for Mockery is that it does not try to be too clever: it does not clone expected parameters like PHPUnit does, allowing you to test their identity with ===.

From great power comes great responsibility

Mockery is too hardcore for me in certain aspects: it can mock static methods and public properties, or intercept autoload functions for mocking new operators. I'm not sure these features are useful apart from testing of legacy code, and they may allow you to follow a problematic design for longer.

For example, consider mocking Demeter chains, in this example taken from the documentation:

$mock = \Mockery::mock('CaptainsConsole');
$mock->shouldReceive('foo->bar->zebra->alpha->selfDestruct')->andReturn('Ten!');

Production code can now call $object->foo()->bar()->zebra()->alpha()->selfDestruct(). However, code which assumes so much about the surrounding object graph is very coupled to other classes and will break whenever its surrounding changes; in this case, the power of the testing framework hinders the design feedback the tests would give you: avoid this chain of calls.

In this sense, Mockery feels like a startup who sells a car which can start really quickly to robbers, so that they can escape the police. :)

Show me the code

The Api of Mockery is magic: you can pass everything as first argument of mock(), such as a name, a class name, or an array of fixed expectation. The same goes for shouldReceive(), which accepts even Demeter chains as you have seen.

Again in the Api we find powerful features like partial mocks implemented as Proxies to a real object. Here is an example of what I found most useful with an eye to object-oriented design:

<?php
use \Mockery as m;

class MockeryExploratoryTest extends PHPUnit_Framework_TestCase
{
    public function testMockASimpleMethodCalledMultipleTimes()
    {
        $service = m::mock('PlanetColorService');
        $service->shouldReceive('getColor')->times(2)->andReturn('blue', 'red');
        $this->assertEquals('blue', $service->getColor('Earth'));
        $this->assertEquals('red', $service->getColor('Mars'));
    }

    public function testMockAnInterface()
    {
        $service = m::mock('ColorService');
        $service->shouldReceive('getColor')->andReturn('blue');
        $this->assertEquals('blue', $service->getColor('water'));
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testClassicThrowingOfAnException()
    {
        $service = m::mock('PlanetColorService'); 
        $service->shouldReceive('getColor')->withAnyArgs()->andThrow(new InvalidArgumentException);
        $service->getColor('Earth');
    }

    /**
     * PHPUnit cannot do this.
     */
    public function testMockWithDifferentReturnValuesForDifferentExpectations()
    {
        $service = m::mock('PlanetColorService');
        $service->shouldReceive('getColor')->with('Earth')->andReturn('blue');
        $service->shouldReceive('getColor')->with('Mars')->andReturn('red');
        $this->assertEquals('blue', $service->getColor('Earth'));
        $this->assertEquals('red', $service->getColor('Mars'));
    }

    /**
     * It neither can do this.
     */
    public function testVerifiesTheOrderOfCallsOnDifferentMethods()
    {
        $service = m::mock('PlanetIdentificator');
        $service->shouldReceive('getPlanet')->withAnyArgs()->ordered();
        $service->shouldReceive('getMoon')->withAnyArgs()->ordered();
        $service->getPlanet('blue');
        $service->getMoon('Earth', 'yellow');
    }

    public function teardown()
    {
        m::close();
    }
}

interface ColorService
{
    public function getColor($planet);
}

class PlanetColorService implements ColorService
{
    public function getColor($planet) {}
}

interface PlanetIdentificator
{
    public function getPlanet($color);
    public function getMoon($planet, $color);
}

I used the following bootstrap:

<?php
require_once 'Mockery/Loader.php';
require_once 'Hamcrest/hamcrest.php';
$loader = new \Mockery\Loader;
$loader->register();

Conclusions

With a purist hat, I would say that in case you must create your Test Doubles with a framework, because they're too complex to mock by hand, you're already in danger.

However Mockery allows you to build mock faster, and it will be very good for testing legacy code. It also requires minimal integration with PHPUnit: just a method call in the teardown() and an autoloader in the bootstrap.

Thus Mockery adds something useful also for TDDing, since it shorten the length of the Red-Green-Refactor cycle; but be aware that you shouldn't resort to certain hacks on new code.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)