Qafoo GmbH - passion for software quality

Help you and your team benefit from new perspectives on cutting-edge quality engineering techniques and tools through the Qafoo team weblog.

By Benjamin Eberlei, first published at Wed, 13 Mar 2013 09:24:48 +0100

Mocking with Phake

Update (14.3.2013): Introduced Test Double wording instead of using mock objects for everything to more cleanly describe the different concepts.

The use of Test Doubles is an important skill to learn when using Test Driven Development (TDD). Test Doubles allow you to replace dependencies of an object with lookalikes, much like crash test dummies are used during automobile safety tests so humans aren't harmed.

Test Doubles Explained

Creating a test double in PHP involves creating a new class that extends the public API of the original class with empty methods. You can safely use all methods of a test double and they will do nothing, rather than calling the original code of the original class. There are two ways to create these test doubles: You can write them yourself or use one of the many existing libraries.

I would strongly recommend to use any of the existing libraries that can simplify and automate this task for you. Technically they work using code-generation at run-time.

To allow interactions with test doubles there are three ways to configure them in any library:

  • Add expectations of the arguments passed to a method (Verification)

  • Add results that are returned from a method-call to the mock object (Stubbing)

  • Delegate calls to the original code (Spying)

Test doubles using the first approach are called Mock Objects. Objects of the second type are called Stubs, of the third type Spies.

Benefits of Test Doubles

There are many reasons why test doubles are useful:

  • Allow units (objects) to be tested in isolation of their dependencies. This is done by replacing the dependencies with test doubles.

  • Allow verification of behavior between objects. In contrast to assertions that can only verify the state of objects in isolation. This makes them very useful with relation to Behavior Driven Development (BDD) inside your unit-tests.

  • Test Doubles are useful to test-drive new interfaces based on required behavior without caring for the implementation at the moment.

That means testdoubles are invaluable to move from state-based object-oriented programming to a behavioral approach based on sending messages between objects.

Introduction to Phake

Using Test Doubles in PHPUnit tests means using the built-in MockObjects library for quite some years. In the last years two contenders emerged that can be used as optional dependencies in PHPUnit:

Both can be installed using Composer and integrated into your projects very easily.

This blog post introduces Phake, because it works quite differently than both PHPUnit Mock Objects and Mockery:

  1. In the well known four phase test with Setup, Exercise, Verify, Teardown both PHPUnit mocks and Mockery require expectations to be part of the "setup" phase.

    This is unfortunate, because mock expectations are much more related to the "Verify" phase instead. This happens in PHPUnit and Mockery, because they don't explicitly differentiate between methods that are mocked (verification) or stubbed (returning results). Phake introduces a differentiation by requiring different APIs to be used for configuration.

  2. Instead of using strings for method names and builder methods for arguments, Phake let's you prototype the method-call in actual PHP code. This simplifies the mock object configuration considerably and requires much less typing.

Let's see an example containing both mock and stub objects in one test for loading the weather data for a given location.

<?php class LoaderTest extends \PHPUnit_Framework_TestCase { public function testGetWeather() { // 1. setup $logger = \Phake::mock('Qafoo\\Weather\\Logger'); $functional = \Phake::mock('Qafoo\\Weather\\Service'); // Stubbing with Phake::when() \Phake::when($functional)->getWeatherForLocation()->thenReturn( new Struct\Weather( 'Fair', 23, 0, 'NW' ) ); $loader = new Loader($functional, $logger); // 2. exercise $locatedWeather = $loader->getWeatherForLocation( new Struct\Location( 'Berlin', 'Germany' ) ); // 3. verify $this->assertInstanceOf( 'Qafoo\\Weather\\Struct\\LocatedWeather', $locatedWeather ); // Verification with Phake::verify() \Phake::verify($logger)->log('Fetched weather for Berlin Germany.'); } }

Using the Phake::when() call and passing a mock object you can prototype what a method call should return for your code to show a desired behavior. See how we can just call ->getWeatherForLocation() as a prototype for how the stub behaves instead of PHPUnits ->method('getWeatherForLocation') or Mockerys ->shouldRecieve('getWeatherForLocation').

Using thenReturn specifies the return value of this method call and completes the description of how the stub works. If you want to return different values on consecutive calls, just chain multiple thenReturn calls. You can use thenThrow to throw exceptions.

Verification is done with Phake::verify() after the tested code was actually exercised. We again prototype what method calls and which arguments we want to verify, in this case ->log('Fetched weather for Berlin Germany'); on the logger mock.

This is a very simple stubbing and verification example with Phake. Comparable to PHPUnit and Mockery, we could add more complex expectations:

  • Using argument matchers to verify the structure of arguments used in method calls.

  • Checking multiple invocations such as exactly n-times, at least N-times or others.

  • Verify no interaction with a mock happened at all.

  • Verify no further interaction happened than the ones already verified.

Conclusion

Phake is a great mocking library and can be easily integrated into PHPUnit. Its new approach to prototype mocks and stubs and the separation between stubbing and verification phases is very refreshing and easy to use.

If you want to go into more detail and learn about Phake, you should check out the extensive documentation.

Comments

  • Jan Marek on Wed, 13 Mar 2013 09:28:39 +0100

    There is one more alternative - mocking framework Mockista created by myself and Jiří Knesl. You can take a look at https://github.com/janmarek/mockista

  • Dorthe on Fri, 15 Mar 2013 09:30:26 +0100

    Are the Qafoo Logger & Weather classes somewhere available? I'd like to play around with the code. This helps me to understand it "really". :-)

  • Mike Lively on Mon, 18 Mar 2013 04:10:33 +0100

    I just found out my site wend down, so the documentation link was not currently working properly. This should be fixed now. Sorry for the inconvenience everybody.