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:
- Mocking Public Properties
- Preserving Pass-By-Reference Method Parameter Behaviour
- (Since Mockery supports only PHP5.3 and up so this means closure is available, )Mock Object Recording
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
- Difference between the Mock Object in PHPUnit and Mockery / Why Mockery?
- Code exmamples of Mockery, Phake, Phockito and PHPUnit / gist: 1753804 / Example temperature webservice
Code To Learn
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.