One of the things that I realised while preparing my Mocks aren't Stubs, Fakes, Dummies or Spies talk, was that none of the major php test double libraries have an API that let's you ask for a particular kind of test double, communicating in your test arrangement what you intend to do with this test double. This led to the inclusion of this slide towards the end of the presentation, showing a quick mockup of how I thought such an API would look for Mockery:

use Mockery\TestDouble as d;

$repo = d::dummy("UserRepository");

$repo = d::stub("UserRepository");
$repo->stub("find")->toReturn(new User());

$repo = d::mock("UserRepository");
$repo->shouldReceive("add"); 

$repo = d::spy("UserRepository");
$repo->shouldHaveReceived("add");

We recently added a Mockery::spy method, which makes a start by configuring the generic test double it returns to ignore any method calls, but I want to take it a step further. If you ask for a Spy, I want you to get a Spy, that will only allow you to stub methods, and verify the calls that have been made against the Spy. That is, it wont allow you to use the mocking style methods, to set up method expectations and verify them later.

Phpspec's prophecy goes some of the way to do this, by locking functionality down as you start to configure the double.


$prophet = new \Prophecy\Prophet; $prophecy = $prophet->prophesize('UserRepository'); $repo = $prophecy->reveal(); // repo is a null object $repo->find(123); // null $repo->findAll(); // null $prophecy ->add($emp = new User) ->shouldBeCalled(); // $repo becomes Mock $repo->find(123); // throws UnexpectedCallException

I like how this works, but it's more of an implicit protocol, I'd like Mockery to do this by contract if possible. I'm also not overly keen on the nomenclature, but I think that's just a personal preference.

This all means we'll need to build a set of individual contracts for each of the double types, so Daniel Karp and I set about hashing it out in our regular pair programming session. Here's the start of the interface for a Spy::

<?php

namespace Mockery\TestDouble;

interface Spy extends Stub
{
    /**
     * @param   string  $method     The method that is expected to be have been called
     *
     * @return MethodCallVerification
     */
    function shouldHaveReceived($method);

    /**
     * @param   string  $method     The method that is expected to not have been called
     * @param   array   $args       The arguments that were expected to not have been passed (optional)
     *
     * @return void
     */
    function shouldNotHaveReceived($method, array $args = array())
}

I'm quite excited about this, but I'm also wary of the many mockery users out there that have been using the existing mockery API. Because of this, as it stands I intend to keep the existing 0.9.x Mockery::mock API around in 1.0, but it will be marked as deprecated. Users will be encouraged to use the new API, to be found at Mockery\TestDouble, which may or may not be pushed back to 2.0.

That's it for now, if you have any comments or feedback, we have an open ended issue for discussions pertaining to a 1.0 release.