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 636 posts at DZone. You can read more from them at their website. View Full User Profile

Practical PHP Testing Patterns: Behavior Verification

01.31.2011
| 5839 views |
  • submit to reddit

Sometimes the System Under Test does not have a final state that we can evaluate, or it does not produce an output to check with explicit assertions. In these cases, it is the interaction with other objects that we can and should define tests on.

We can still have some assertions if we want to mix a bit of State Verification in our Behavior Verification test. This pattern's name means that we verify the external behavior of the SUT (what methods it calls) instead of the final result, usually because a final result would take into account too many objects to produce, while the focus of the current test is on the responsibility of a single class.

Isolation

For the purpose of isolating the SUT from other code in the system, we do not inject real objects as collaborators in it. Instead, we use Test Doubles (which are a prerequisite for Behavior Verification), and in particular the specialization of Test Doubles named Mock.

Mocks are implementation of a contract (interface, or abstract class, or even concrete class) that instead of doing real work, perform assertions on the calls that are made to them.

They can also return canned results like Stubs, but their fundamental trait is performing assertions: on parameters, on the number of calls, on their order, and so on.

Test Doubles can usually be generated automatically by PHPUnit on the fly, so you don't have to code them by hand nor to maintain their generated code manually. PHPUnit provides the getMock() method to produce them in a Testcase Object, even when sometimes Stubs and simpler Test Doubles may be generated with the same Api method.

Variations

  • In Procedural Behavior Verification, the Mock records the calls and their data, to expose them to the Testcase Object later. Assertions can be made on these recordings.
  • Expected Behavior Specification is usually applied instead. The definition of what methods on a Mock should be called, and passing what, is done beforehand. This part of the arrange phase is called defining expectations.

In TDD

In Test-Driven Development, we write tests on the SUT that may use Mocks before the equivalent real implementation of those mocks (sometimes even before we know that a Mock is needed).  The colalborator's Api should be defined by the point of view of the caller, not of the implementor. This ensures it will be simple to write mock expectations, and that the contract between the two classes does not result in high coupling.

Another principle to keep in mind for the same reason is to only mock types you own: you should never mock directly classes which you do not have control on. It will only result in verbose and fragile expectations. You can insert a wrapping layer or some State Verification instead.

Examples

The example extend the Car class of the State Verification article to provide an interaction with another class, Engine. Only the interface is provided since it is not important which class will implementa it. The calls of Car to Engine under different scenarios are the subjects of the test.

<?php

class BehaviorVerificationTest extends PHPUnit_Framework_TestCase
{
private $car;
private $engine;

public function setup()
{
$this->engineMock = $this->getMock('Engine');
$this->car = new Car($this->engineMock);
}

/**
* Expected Behavior Verification on the SUT.
* Maybe this test is a bit too all-encompassing as it also checks the Car
* is obtaining its speed measurement from the Engine.
*/
public function testCarShouldSendEngineTheFuelToAccelerate()
{
$this->engineMock->expects($this->once())
->method('fuel')
->with(2)
->will($this->returnValue(40));

$this->car->accelerate(10);

$this->assertEquals(40, $this->car->getSpeed());
}

/**
* Again Expected Behavior Verification. This is a pure Behavior Verification
* example as there are no assertions other than the one made by the mock.
*/
public function testCarShouldNotSendFuelIfThereIsNoPositiveAcceleration()
{
$this->engineMock->expects($this->never())
->method('fuel');

$this->car->accelerate(0);
}

public function testCarContinuesToSendFuel()
{
$this->engineMock->expects($this->exactly(2))
->method('fuel');

$this->car->accelerate(10);
$this->car->accelerate(20);
}
}

class Car
{
private $engine;
private $fuelFactor = 0.2;

public function __construct(Engine $engine)
{
$this->engine = $engine;
}

public function accelerate($speedDelta)
{
if ($speedDelta > 0) {
$fuel = $speedDelta * $this->fuelFactor;
$this->speed = $this->engine->fuel($fuel);
}
}

public function getSpeed()
{
return $this->speed;
}
}

/**
* A Concrete implementation of this interface is not needed.
* The test is really at the unit level: no other classes are involved.
* However, Behavior Verification can be performed also by mocking concrete classes,
* at the price of having greater coupling of the test to the various classes.
*/
interface Engine
{
public function fuel($fuelAmount);
}
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.)