48慈眼 *Reloaded*

Mock Object in PHPUnit ROCKS

03 Nov 2012

Suppose you fake it like this:

class TwitterClient
{
    public function tweet($message)
    {
        return true;
    }
}

$foo = new TwitterClient();
$foo->tweet('Spot ko!'); // returns true

With PHPUnit, you’re able to use Mock Object instead. Now your code looks like:

class ClientContainerTest extends PHPUnit_Framework_TestCase
{
    public function test_MockPractice()
    {
        $foo = $this->getMock('TwitterClient', array('tweet'));
        $foo->expects($this->any())
            ->method('tweet')
            ->will($this->returnValue(true));

        $foo->tweet('Spot ko!'); // returns true
    }
}

It’s quite easy to understand how method(), will() and returnValue() work. How about expects()? It restricts the number of invocation count. Set any() then no restriction will be set. If you would like some method to be invoked only once, then set once() for example.

This is how you create Mock Object in PHPUnit, however it’s still hard to find out why Mock Object is useful. Let’s take another example, which accesses with database server via PDO.

Supporse you would like to run your test but you can’t run database server. I’m showing you a bad example first:

class PDO
{
    public function __construct($dsn)
    {
        return true;
    }

    public function query($sql)
    {
        return new PDOStatement();
    }
}

$pdo = new PDO('sqlite:memory'); // Fatal Error
$pdo->query('SELECT * FROM users;');

Run the code above, then you will see error message on your display which says “Cannot redeclare class PDO”. As PDO is embedded class and declared already, you cannot redeclare PDO. To use sqlite:memory which enable you to create database on memory was pretty neat though.

This is the case where you should remember Mock Object.

class PDOContainerTest extends PHPUnit_Framework_TestCase
{
    public function test_StubQuery()
    {
        $foo = $this->getMock('PDO', array('query'), array('sqlite:memory'));
        $foo->expects($this->any())
            ->method('query')
            ->will($this->returnValue(new PDOStatement()));

        $foo->query('SELECT * FROM users;') // instantiated PDOStatement class will be returned
        $foo->getAvailableDrivers(); // take note that methods originally declared in PDO class is available too
    }
}

With this test code, $foo will have stubbed query() method as well as methods originally declared in PDO class such as getAvailableDrivers() in the example above. This is how Partial Mock is made.

If you only need to prepare fixture for DB for example, then you have other even better choises like Database Extension of PHPUnit or Phactory etc. But how about your legacy code is doing for example:

  • Calling some webservice method(API) and depending on the behavior of the external module
  • Depending on the volatile data which memcached keeps at the production environment

would you still like to search the web and run out of your limited time?

To learn mock framework which doesn’t focus on solving problem in specific domain is pretty useful. Whatever issues you are facing with, mock framework gives you higher testability of the desgin you are making, which means refactoring becomes much easier for you.

Go on the next chapter and let’s take a look at the various mock frameworks written in PHP.

Mock Frameworks Written in PHP

Here is the result of my quick investigation on Oct 2012.

Framework Required PHP Environment Website Note
PHPUnit PHPUnit3.7: require PHP 5.3.3 and up(PHP 5.4.7+ is recommended)
PHPUnit3.6: require even with PHP 5.2
Website Sample Code
Mockery PHP 5.3.2 and up Website Sample Code
Phake PHP 5.2.4 and up Website Sample Code
SimpleTest SimpleTest1.1: require PHP 5.0.3 and up
Older version of Simpletest work with PHP 4
Website Sample Code
Phockito (Not Yet Confirmed) Website N/A
yayMock (Not Yet Confirmed) Website N/A

From my quick skim, PHPUnit and Mockery look useful enough to me. And then, What is the pros/cons of these two mock frameworks and finally how to pick one?

The table below shows what feature is simple comparison of mock framework features.

Functionality PHPUnit 3.7 Mockery 0.7.2 Note
Invocation Count Constraint OK Love It Mockery is better. atMost() and atLeast() methods are available.
Partial Mocking OK Very Good Constructer can be much simpler in Mockery.
Argument Matchers OK OK All mathers at table 4.3 are available in PHPUnit by with() call. About Mockery, refer README.md
Cascading Mocks|center OK Excellent Just a bit hustle with PHPUnit but doable or (like this). With Mockery, here is example in README.md
Ordered Expectations Can’t OK With Mockery it’s possible but not comfortable

As you can see the table, Mockery 0.7.2 is definitely better in terms of functionality. Actually Mockery is better at other aspects as well:

Then what about PHPUnit3.7? In my opinion, it’s still useful if you are just newbie on using mock framework.

Lately PHPUnit is broadly used in PHP development and therefore, once you learn to write mock object then for sure, you will be able to apply your experience to the design issue your team will face.

Reference

Code To Learn

Goos Book

You must know about the book “Growing Object-Oriented Software Guided by Tests” a.k.a #goos by Steve Freeman and Nat Pryce. And you also know that there was a book Steve and Nat contributed about same topic before goos?

“Mock Roles, not Objects” is it. The pdf just consists of 11 pages, however the concept and the idea they explained in the book is worth reading. I promise that you will not waste your time to read this book deliberately.

Various Sample Codes Are Available

I wrote already in both PHPUnit nad Mockery and they are available at my github account. Kindly feel free to send me pull request if you find something wrong about my code. Other than that, @iakio seems wrote in PHP as well.

Or if you are Java developer, then the example by @digitalsoul0124 may help. There are tons of example in the world wide web so that you can find your favorite example with your favorite programming language. ( Let me add that in Java the example in jmock1 and the test, or there is also example in jmock2)

Tips On Coding “Mock Roles, not Objects”

  • Better to know basics about OOP and design patterns. Text implies useful and practical idea of OOP such as “object oriented style based on composition rather than inheritance” or “Programming by Composition” but if you don’t know OOP, these words will not make sense at all.

  • To set up PHPUnit environment, tips by suin works to me.(Sorry it’s written only in Japanese, but you can guess by reading just commands and source codes;) ) If you would like to use under 5.2 where you can’t use composer unfortunately, you need to use PEAR instead. – Take note that phar(PHP Archive) becomes available from PHPUnit3.7.5

  • It would be small point but let me tell you as It was a bit surprising to me. In PHPUnit, once you call getMock() then it increments number of assertions of final test run result even without calling any expects to the instantiated mock object. Guessing verify() equivalent function would be called at the end of test running. This unexpected things robbed me from a few hours to get understand well.

  • You CANNOT do method chaining with using getMock() instance directly(I found someone at stackoverflow did completely same as me). For example, this code will cause error:

          $foo = $this->getMock('PDO', array('query'))
              ->expects($this->any())
              ->method('query')
              ->will($this->returnValue(array('id'=>7, 'name'=>'Ichiro Suzuki')));
    
  • HashMap doesn’t make sense to most of PHP programmers. In PHP, let’s say it’s just a Array.(* I know strictly speaking Hashmap in Java is different from Array in PHP AT ALL but here just let me please explain just for simplification. [[Go stackoverflow>http://stackoverflow.com/questions/6841379/is-there-java-hashmap-equivalent-in-php]] if you would like to keep talking about this)

  • In the original pdf document, you’ll frequently see verify() method are called, but in PHPUnit, verify() equivalent function will be called automatically at the end of test running, so you do not need to take care about verify() calling in case you would use PHP. ( use mockery_verify in Mockery and verify() in Phake instead)

  • Inner class is used at some code snippet but in PHP inner class is not supported. Just ignore and take those class out of class is enough so far.

That’s all for today. Mock object in PHPUnit ROCKS.

Related Posts

Setting Up PHP5.4/Composer Application on Windows With #Chocolatey

RubyConf 2016

Bytecode cache is experimentally released in Ruby2.3

 

about me

@remore is a software engineer, weekend contrabassist, and occasional public speaker. Read more