[Php] Mock 有 Reference Parameter 的 Method

前言

雖然盡量不要在 php 裡面寫 call by reference 的參數,但是有一些舊有的code 還是有人這樣寫。如果這時又想要mock 他就麻煩了,因為值並不會隨著自已想要的內容作改變。

實作

原先的 class 設計如下

[code lang=“php”] <?php class NeedToMockClass { function testReference(&$str) { // 也許會有很多很複雜的計算,不過這邊簡化成一行就好 $str = “test String”; return; } } class testClass{ function test($str) { $t = new NeedToMockClass(); $t->testReference($str); return __METHOD__ .’->’ . $str; } } // $t = new testClass(); // $str = $t->test(’not a test string’); // var_dump($str); // string(28) “testClass::test->test String” [/code]

How To Test?

如果這時我要測 testClass 我該如何測?因為我單純的只想測 testClass 裡的邏輯對不對,我不想管 NeedToMockClass 的邏輯,所以這時我該把 class mock 掉,並且讓他可以自定 $str 的 output。 首先,會先改寫 testClass 讓他可以從外面傳 NeedToMockClass 的 object 進去。 [code lang=“php”] <?php class testClass{ private $_mockClass = null; // 由 construct 傳進來,讓他可以取代掉預設的 mockClass function __construct($mockClass == null) { if ($mockClass = null) { $mockClass = new NeedToMockClass(); } $this->_mockClass = $mockClass; } function test($str) { // 接著用mock 後的class 來執行 $this->_mockClass->testReference($str); return __METHOD__ .’->’ . $str; } } [/code] 最後測試程式會用下面這個方法寫,主要是用 returnCallback 的方式來 mock: [code lang=“php”] <?php require_once ‘./sample.php’; class testTest extends PHPUnit_Framework_TestCase { function mockTestReference(&$str) { $str = ‘3345678’; } function testTestClass() { $mockClass = $this->getMock(‘NeedToMockClass’, array(), array(), "" ,false); // 這裡是用 returnCallback 的方式呼叫 testTest::mockTestReference 這樣就可以改 $str 的內容了 $mockClass->expects($this->any()) ->method(’testReference’) ->will($this->returnCallback(array(’testTest’, ‘mockTestReference’))); $t = new testClass($mockClass); $str = ‘one test’; $ret = $t->test($str); // 最後會如預期的產生 testClass::test->3345678 的字樣 $this->assertEquals(’testClass::test->3345678’, $ret); } } [/code] 順利達成目標,其結果如下: [code lang=“bash”] $ phpunit testTest.php PHPUnit 3.7.35 by Sebastian Bergmann. . Time: 22 ms, Memory: 3.50Mb OK (1 test, 2 assertions) [/code]

參考文件