Method Parameter Matchers

The verification and stubbing functionality in Phake both rely heavily on parameter matching to help the system understand exactly which calls need to be verified or stubbed. Phake provides several options for setting up parameter matches.

The most common scenario for matching parameters as you use mock objects is matching on equal variables For this reason the default matcher will ensure that the parameter you pass to the mock method is equal (essentially using the ‘==’ notation) to the parameter passed to the actual invocation before validating the call or returning the mocked stub. So going back to the card game demonstration from the introduction. Consider the following interface:

interface DealerStrategy
{
    public function deal(CardCollection $deck, PlayerCollection $players);
}

Here we have a deal() method that accepts two parameters. If you want to verify that deal() was called, chances are very good that you want to verify the the parameters as well. To do this is as simple as passing those parameters to the deal() method on the Phake::verify($deal) object just as you would if you were calling the actual deal() method itself. Here is a short albeit silly example:

//I don't have Concrete versions of
// CardCollection or PlayerCollection yet
$deck = Phake::mock('CardCollection');
$players = Phake::mock('PlayerCollection');


$dealer = Phake::mock('DealerStrategy');

$dealer->deal($deck, $players);

Phake::verify($dealer)->deal($deck, $players);

In this example, if I were to have accidentally made the call to deal() with a property that was set to null as the first parameter then my test would fail with the following exception:

Expected DealerStrategy->fooWithArgument(equal to
<object:CardCollection>, equal to <object:PlayerCollection>)
to be called exactly 1 times, actually called 0 times.
Other Invocations:
  PhakeTest_MockedClass->fooWithArgument(<null>,
equal to <object:PlayerCollection>)

Determining the appropriate method to stub works in exactly the same way.

There may be cases when it is necessary to verify or stub parameters based on something slightly more complex then basic equality. This is what we will talk about next.

Using PHPUnit Matchers

Phake was developed with PHPUnit in mind. It is not dependent on PHPUnit, however if PHPUnit is your testing framework of choice there is some special integration available. Any constraints made available by the PHPUnit framework will work seemlessly inside of Phake. Here is an example of how the PHPUnit constraints can be used:

class TestPHPUnitConstraint extends PHPUnit_Framework_TestCase
{
    public function testDealNumberOfCards()
    {
        $deck = Phake::mock('CardCollection');
        $players = Phake::mock('PlayerCollection');

        $dealer = Phake::mock('DealerStrategy');
        $dealer->deal($deck, $players, 11);

        Phake::verify($dealer)
            ->deal($deck, $players, $this->greaterThan(10));
    }
}

I have added another parameter to my deal() method that allows me to specify the number of cards to deal to each player. In the test above I wanted to verify that the number passed to this parameter was greater than 10.

For a list of the constraints you have available to you through PHPUnit, I recommend reading the PHPUnit’s documentation on assertions and constraints. Any constraint that can be used with assertThat() in PHPUnit can also be used in Phake.

Using Hamcrest Matchers

If you do not use PHPUnit, Phake also supports Hamcrest matchers. This is in-line with the Phake’s design goal of being usable with any testing framework. Here is a repeat of the PHPUnit example, this time using SimpleTest and Hamcrest matchers.

class TestHamcrestMatcher extends UnitTestCase
{
    public function testDealNumberOfCards()
    {
        $deck = Phake::mock('CardCollection');
        $players = Phake::mock('PlayerCollection');

        $dealer = Phake::mock('DealerStrategy');
        $dealer->deal($deck, $players, 11);

        Phake::verify($dealer)->deal($deck, $players, greaterThan(10));
    }
}

Parameter Capturing

As you can see there are a variety of methods for verifying that the appropriate parameters are being passed to methods. However, there may be times when the prebuilt constraints and matchers simply do not fit your needs. Perhaps there is method that accepts a complex object where only certain components of the object need to be validated. Parameter capturing will allow you to store the parameter that was used to call your method so that it can be used in assertions later on.

Consider the following example where I have defined a getNumberOfCards() method on the CardCollection interface.

interface CardCollection
{
    public function getNumberOfCards();
}

I want to create new functionality for a my poker dealer strategy that will check to make sure we are playing with a full deck of 52 cards when the deal() call is made. It would be rather cumbersome to create a copy of a CardCollection implementation that I could be sure would match in an equals scenario. Such a test would look something like this.

Please note, I do not generally advocate this type of design. I prefer a dependency injection versus instantiation. So please remember, this is not an example of clean design, simply an example of what you can do with argument capturing.

class MyPokerGameTest extends PHPUnit_Framework_TestCase
{
    public function testDealCards()
    {
        $dealer = Phake::mock('MyPokerDealer');
        $players = Phake::mock('PlayerCollection');

        $cardGame = new MyPokerGame($dealer, $players);

        Phake::verify($dealer)->deal(Phake::capture($deck), $players);

        $this->assertEquals(52, $deck->getNumberOfCards());
    }
}

You can also capture parameters if they meet a certain condition. For instance, if someone mistakenly passed an array as the first parameter to the deal() method then PHPUnit would fatal error out. This can be protected against by using the the Phake::capture()->when() method. The when() method accepts the same constraints that Phake::verify() accepts. Here is how you could leverage that functionality to bulletproof your captures a little bit.

class MyBetterPokerGameTest extends PHPUnit_Framework_TestCase
{
    public function testDealCards()
    {
        $dealer = Phake::mock('MyPokerDealer');
        $players = Phake::mock('PlayerCollection');

        $cardGame = new MyPokerGame($dealer, $players);

        Phake::verify($dealer)->deal(
            Phake::capture($deck)
                ->when($this->isInstanceOf('CardCollection')),
            $players
        );

        $this->assertEquals(52, $deck->getNumberOfCards());
    }
}

This could also be done by using PHPUnit’s assertions later on with the captured parameter, however this also has a side effect of better localizing your error. Here is the error you would see if the above test failed.

Exception: Expected MyPokerDealer->deal(<captured parameter>,
equal to <object:PlayerCollection>) to be called exactly 1
times, actually called 0 times.
Other Invocations:
  PhakeTest_MockedClass->fooWithArgument(<array>,
<object:PlayerCollection>)

It should be noted that while it is possible to use argument capturing for stubbing with Phake::when() I would discourage it. When stubbing a method you should only be concerned about making sure an expected value is return and argument capturing in no way helps with that goal. In the worst case scenario you will have some incredibly difficult test failures to diagnose.

Custom Parameter Matchers

An alternative to using argument capturing is creating custom matchers. All parameter matchers implement the interface Phake_Matchers_IArgumentMatcher. You can create custom implementations of this interface. This is especially useful if you find yourself using a similar capturing pattern over and over again. If I were to rewriting the test above using a customer argument matcher it would look something like this.

class FiftyTwoCardDeckMatcher implements Phake_Matchers_IArgumentMatcher
{
    public function matches($argument)
    {
        return ($argument instanceof CardCollection
            && $argument->getNumberOfCards() == 52);
    }

    public function __toString()
    {
        return '<object:CardCollection with 52 cards>';
    }
}

class MyBestPokerGameTest extends PHPUnit_Framework_TestCase
{
    public function testDealCards()
    {
        $dealer = Phake::mock('MyPokerDealer');
        $players = Phake::mock('PlayerCollection');

        $cardGame = new MyPokerGame($dealer, $players);

        Phake::verify($dealer)->deal(new 52CardDeckMatcher(), $players);
    }
}