2015
Feb
05

当一个 PHP 程式有 require 到多个 library 时,Unit test 会变得很难完成,或者是根本无法测试,而 Mock Object 就是一个用来处理这种很难测试的 PHP 程式。

下列这个网址是 phpunit 官方文件。


PHP Mock Object 可以覆写 class 中Method 的实作方式,直接定义 return 结果,不但如些,它还能定义第一次呼叫,与第二次,第 N 次呼叫的 return 结果,让每一次都有不同的 return 值,对於那种常常会被 Call 超过一次的 Method 来说,不用 Mock Object 的话,工程师就得写很多 Test 程式来伪造 Method。

使用 Mock Object 时,要注意 Private 与 Protected 属性的 Method 与 Property 都是不能够被 Mock 掉的,如果你的程式用到很多 Private 或是 Protected ,就一样很容易写 Unit Test 时会卡住...

Method 不能是 Private 或是 Protected
Property 不能是 Private 或是 Protected

Mock Object 范例

我先定义了一个简单的 class ,里面有两个 Method 分别是 calculate 与 getPrice,而 getPrice 又会去 call calaulate ,当我要测试 getPrice 时,就会被迫连 calaulate 这个 Method 也一起测试,这时我就可以用 Mock Object 来定义 calculate 的 return 值,单纯的测试 getPrice。

php source code
  1. //
  2. class MyClass {
  3. public function calaulate() {
  4. ...
  5. }
  6.  
  7. public function getPrice() {
  8. $priceInfo = $this->calaulate();
  9. $totalPrice = $priceInfo['sumPrice'] + $priceInfo['fee'];
  10. return $totalPrice;
  11. }
  12. }
Tester for using Mock Builder
  1. //
  2. class testMyClass extends PHPUnit_Framework_TestCase {
  3.  
  4. public function test_getPrice() {
  5. $tester = $this->getMockBuilder('MyClass')
  6. ->setMethods(array("calculate"))
  7. ->getMock();
  8.  
  9. $tester->expects($this->any())
  10. ->method('calculate')
  11. ->will($this->returnValue(array("sumPrice" => 979, "fee" => 21)))
  12. $totalPrice = $tester->getPrice();
  13. $this->assertEquals(1000, $totalPrice);
  14. }
  15.  
  16. }

Mock constructor

constructor
  1. //
  2. class testMyClass extends PHPUnit_Framework_TestCase {
  3.  
  4. public function test_getPrice() {
  5. $tester = $this->getMockBuilder('MyClass')
  6. ->setConstructorArgs(array("p1", "p2"));
  7. ->setMethods(array("calculate"))
  8. ->getMock();
  9. }
  10.  
  11. }

回應 (Leave a comment)