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)