PHP, Zend Framework and Other Crazy Stuff
Zend Framework
Mockery: From Mock Objects to Test Spies
May 23rd
With next week seeing the formal release of Mockery 0.6 which is currently fermenting on Github at http://github.com/padraic/mockery, I’m already looking forward to next piece of the puzzle arriving with 0.7. Mockery is an opportunity to being something new and fresh to the PHP mock objects environment beyond a far neater flexible API.
If you follow the test double debate in other languages there are two popular concepts making their presence felt. The first is that the over-specified definitions of test doubles (we have dummy objects, test stubs, mock objects, test spies and fake objects) do more harm than good since in reality we never really distinguish (nor care to) between most of them. In most cases we just sort of wing it, using our own concepts and intent to create test doubles which do what we want with little thought as to the kind of test double we’re creating. In the end, the definitions all merge together into a spectrum of possible behaviours we add as and if needed.
The second is that we’re seeing a lot more attention over whether Mock Objects are the best way to do things. Nobody doubts they have the right idea with Mock Objects, but the upfront setting of expectations may come across as unintuitive. A typical test begins with exercising code, and thereafter we typically make assertions on the behaviour (whether it be a resulting state change or an object interaction). Mock Objects reverse that – we expect first, and then we exercise the code.
With the future (before end of June) 0.7 release, Mockery will offer an alternative approach to the traditional Expect-Exercise-Verify cycle called the Test Spy. 0.6 already offers a measure of that capability through its ability to record interactions, though recording is completely automated and geared primarily towards comparing two sets of source code which should behave identically (e.g. during refactoring). The new Test Spy implementation will be API driven, using a similar form to Mockery’s existing Mock Object approach.
Why Test Spies?
Using Test Spies returns to the test based approach of exercising code first, and then making assertions. There’s a reason why this cyclical approach is used in unit testing. It’s easy to understand and makes sense from the start. Applying it to what we do with Mock Objects can make using Test Doubles in general easier to learn and teach. It may also remove barriers to entry for those who simply never understood Mock Objects or loathed their expectation setup. Test Spies also operate on a selective basis – where Mock Objects require upfront expectations to define how they will behave, Test Spies just do what you need them to and then allow selective assertions afterwards (i.e. you only need to test what you want to test).
The difference, once you see the code, is very subtle. Not unusually for testing practices, subtle differences in approach rely on the YMMV yardstick. Some people prefer the upfront expectation setting, others find the after-use assertion approach easier to understand or as more applicable to their thinking.
By supporting both approaches as equal citizens, Mockery once again lets go of the proverbial rope around your neck tied there by Java traditions. You’ll be free to go with whatever approach you find yourself preferring. Better, they can both co-exist in relative peace since they are drawn from the same framework and both share nearly all of the exact same API elements, with the exact same meaning. Whether you prefer Mock Objects or Test Spies (both equally are capable of being used as Test Stubs), they are both equally understandable by anyone using Mockery.
As a means of showing the differences, here are two test cases. The first is written using the Mock Object terminology, the second using the Test Spy terminology – each actually achieves the exact same goal.
[geshi lang=php]use \Mockery as M;
class StarshipTest extends PHPUnit_Framework_TestCase
{
public function testEngagingWarpDrive()
{
$engineering = M::mock(‘Engineering’);
$engineering->shouldReceive(‘prepForWarpFactor’)->with(8)->once()->andReturn(true);
$engineering->shouldReceive(‘engage’)->once();
$starship = new Starship($engineering);
$starship->accelerateToWarp(8);
}
}[/geshi]
Or, using a Test Spy approach.
[geshi lang=php]use \Mockery as M;
class StarshipTest extends PHPUnit_Framework_TestCase
{
public function testEngagingWarpDrive()
{
$engineering = M::mock(‘Engineering’);
$engineering->whenReceives(‘prepForWarpFactor’)->thenReturn(true);
$starship = new Starship($engineering);
$starship->accelerateToWarp(8);
$engineering->assertReceived(‘prepForWarpFactor’)->with(8)->once();
$engineering->assertReceived(‘engage’)->once();
}
}[/geshi]
To demonstrate the selective assertion approach, what if we just didn’t care about whether or not the engage() method was used? In that case, we could just drop it from the assertions altogether – never mentioning it in the test at all. In the Mock Object approach, we cannot do this – mocking means we must set all methods expected (otherwise the methods would not exist on the mock). We could even drop the assertion on prepForWarpFactor, although we still need to retain the stubbing of its return value (since it’s needed).
Supporting Test Spies and Mock Objects also has the other obvious benefit that you can switch modes effortlessly. There will always be cases where expectation setting is preferable over assertions and vice versa. Both have their uses even if you heavily prefer any one over the other.
Watch for the Mockery 0.6 release next week! I’ll follow through with Test Spies in 0.7 once I get the final API down (working on making it even shorter than our Mock Object API can achieve).
The Mockery: PHP Mock Objects Made Simple
May 20th
As I explored in my previous post, PHP Mock Objects: Sucking Ass Since Forever, Mock Objects in PHP have had difficulty gaining traction. One of the targets of this examination (in between variations of “I love PHPUnit”) was PHPUnit’s Mock Objects implementation. My main point, I suppose, was that with PHPUnit being the de-facto standard for unit testing, its mock objects implementation left a lot to be desired. As the later comments suggested, suger coating the situation won’t change the fact I’m criticising one of the most deeply embedded libraries in PHP practice (well, actually a teeny tiny bit of it).
So what is Mockery? Mockery is a mock object framework. You can use it to mock, stub and dazzle your classes into submission – even when they don’t exist yet
. Mock objects are almost a given when it comes to Test-Driven Development, but even non-TDD unit testing can use mocks and stubs. They encourage test isolation, stand in for classes you haven’t implemented yet, and can replace resource intensive tasks with mocked versions (since we like our tests to run really fast). Using Mock Objects (or Stubs) is just incredibly useful.
So what does Mockery offer you, the unit tester?
1. It integrates with pretty much any conceivable testing framework with almost no effort.
2. It’s simple to use, intuitive, and specifically works with the language we use to describe object interactions.
3. The API is uncluttered and minimal.
4. Flexibility is its cornerstone allowing it to accurately and succintly express your requirements.
5. It enables simple one-off setups that can be later modified so you don’t need to re-specify entire mocks for every single test.
6. If you use Ruby and PHP, it will make your day (yes, I also write Ruby – sue me).
Why consider it instead of PHPUnit’s or (insert PHP testing framework)’s mock object implementation?
1. It ports to ANY testing framework with zero effort
2. It isn’t inspired by Java
3. It has no gotchas, requirements for custom callback code, or complicated and frustrating setups.
4. It’s capable of mocking real objects and performing mock object recordings of existing processes.
5. Expectations are not pre-constrained – you hold the rope.
Time for the inevitable API exploration… Mockery is written for PHP 5.3, so get your namespace booties on. If you cannot currently adopt PHP 5.3, read along anyway. You’ll be using PHP 5.3 eventually
.
You can find Mockery on Github at http://github.com/padraic/mockery. Besides the code, there is one massive README that will go into the API and usage in far more detail than I can do in this article. It is currently not released to a PEAR channel, but I’ll be doing so once I feel happy that the framework has enough hard-core crazy features to last its lifetime in PHP. You can, however, clone from the git repository and install it via PEAR using the included package.xml file. I love issue reports – if you have them, don’t be stingy and keep them to yourself.
For those who find themselves curious about how, specifically, Mockery pits against PHPUnit Mock Objects, I’ll run a later article contrasting the two in terms of features and examples.
The basis of Mockery’s API is the English language. It’s a Domain Specific Language (DSL) in the form of a fluent interface. Given that basis, it’s not surprising that Mockery’s intent is to capture every possible object expectation within a DSL statement that reads like English.
For example, let’s assume we have the class Engineering which is utilised by the class Starship. Hey, I wrote a whole article based on a dodgy Klingon once so get with the programme. We’re testing the Starship class, and find that there is no Engineering class…yet. So we shrug, and decide to use a mocked object to replace Engineering. One of the consequences of this approach is that, down the line, the mock usage will actually specify the API our real Engineering class should use (this is test-driven DESIGN, afterall).
To boot up the creative juices, we start with a story:
Given our Starship has located a planet, it will instruct Engineering to turn off the warp drive, and then to divert 40% power to sensors, and then to divert 20% power to auxilliary engines, and finally to run a Level-1 diagnostic on itself. All instructions must be given once in this precise order. There may optionally be many diagnostic runs of any Level at any stage in the process. The Starship must report whether the operation was successful. Success is measured by successfully passing a Level 1 diagnostic.
Okay, so what exactly are we expecting?
[geshi lang=php]use \Mockery as M;
require_once ‘Starship.php’;
class StarshipTest extends PHPUnit_Framework_TestCase
{
public function testEngineeringResponseToEnteringOrbit()
{
$mock = M::mock(‘Engineering’);
$mock->shouldReceive(‘disengageWarp’)->once()->ordered();
$mock->shouldReceive(‘divertPower’)->with(0.40, ‘sensors’)->once()->ordered();
$mock->shouldReceive(‘divertPower’)->with(0.30, ‘auxengines’)->once()->ordered();
$mock->shouldReceive(‘runDiagnosticLevel’)->with(1)->once()->andReturn(true)->ordered();
$mock->shouldReceive(‘runDiagnosticLevel’)->with(M::type(‘int’))->zeroOrMoreTimes();
$starship = new Starship($mock);
$this->assertTrue($starship->enterOrbit());
}
public function teardown()
{
M::close();
}
}[/geshi]
There’s the test. Our mock has been setup according to the expectations implicit in the feature story. As you might notice, the final diagnostic calls are unordered (can occur at any time between ordered calls). For PHPUnit, the teardown() method is optional if you configure PHPUnit to use the bundled TestListener. I added it to show that it’s the sole integration needed with a testing framework (unless using the bundled TestListener for PHPUnit).
Now let’s write just enough code to pass this test in Starship.php:
[geshi lang=php]class Starship
{
protected $_engineering = null;
public function __construct($engineering)
{
$this->_engineering = $engineering;
}
public function enterOrbit()
{
$this->_engineering->disengageWarp();
$this->_engineering->runDiagnosticLevel(5); // unordered call!
$this->_engineering->divertPower(0.40, ‘sensors’);
$this->_engineering->divertPower(0.30, ‘auxengines’);
$criticalResult = $this->_engineering->runDiagnosticLevel(1);
return $criticalResult;
}
}[/geshi]
Hurrah! The test passes. We’ve just tested Starship without needing a real Engineering object (or even a class of that name!). You can find this example in the examples directory on Github for Mockery, along with the phpunit.xml configuration and Bootstrap.php file used.
If something did go wrong with our expectations, Mockery would throw an Exception to complain about it.
The Mockery API also handles return values (including a return queue for multiple method invocations with the same arguments), of course, along with a host of other features and expectation term methods. Refer to the README up on Github. Like I said before, it’s very complete and informative. We’ve barely touched on the basics of Mockery in this article, so get reading.

Recent Comments