2013-04-09 125 views

回答

15

有一對夫婦現有解決方案的這個問題已經上網,但所有我見過的那些共享一個類似的弱點:他們依靠->expects($this->at(n))。 PHPUnit中的'expect at'函數具有稍微奇怪的行爲,因爲計數器用於每個方法調用模擬。這意味着如果你有一個直接的foreach之外的方法調用你的迭代器,你必須調整你的迭代器模擬。

的解決方案是創建一個對象保持基本迭代數據(源陣列和位置),並通過該進入returnCallback閉包。因爲它是通過引用傳遞的,所以對象在調用之間保持最新,所以我們可以模擬每個方法來模擬一個簡單的迭代器。現在,我們可以像平常一樣使用迭代器模擬,而不必擔心嚴格的呼叫順序。低於

的抽樣方法可以用來設置模擬的迭代器:如果你只需要測試針對通用的迭代,那麼PHP(在SPL擴展

/** 
* Setup methods required to mock an iterator 
* 
* @param PHPUnit_Framework_MockObject_MockObject $iteratorMock The mock to attach the iterator methods to 
* @param array $items The mock data we're going to use with the iterator 
* @return PHPUnit_Framework_MockObject_MockObject The iterator mock 
*/ 
public function mockIterator(PHPUnit_Framework_MockObject_MockObject $iteratorMock, array $items) 
{ 
    $iteratorData = new \stdClass(); 
    $iteratorData->array = $items; 
    $iteratorData->position = 0; 

    $iteratorMock->expects($this->any()) 
       ->method('rewind') 
       ->will(
        $this->returnCallback(
         function() use ($iteratorData) { 
          $iteratorData->position = 0; 
         } 
        ) 
       ); 

    $iteratorMock->expects($this->any()) 
       ->method('current') 
       ->will(
        $this->returnCallback(
         function() use ($iteratorData) { 
          return $iteratorData->array[$iteratorData->position]; 
         } 
        ) 
       ); 

    $iteratorMock->expects($this->any()) 
       ->method('key') 
       ->will(
        $this->returnCallback(
         function() use ($iteratorData) { 
          return $iteratorData->position; 
         } 
        ) 
       ); 

    $iteratorMock->expects($this->any()) 
       ->method('next') 
       ->will(
        $this->returnCallback(
         function() use ($iteratorData) { 
          $iteratorData->position++; 
         } 
        ) 
       ); 

    $iteratorMock->expects($this->any()) 
       ->method('valid') 
       ->will(
        $this->returnCallback(
         function() use ($iteratorData) { 
          return isset($iteratorData->array[$iteratorData->position]); 
         } 
        ) 
       ); 

    $iteratorMock->expects($this->any()) 
       ->method('count') 
       ->will(
        $this->returnCallback(
         function() use ($iteratorData) { 
          return sizeof($iteratorData->array); 
         } 
        ) 
       ); 

    return $iteratorMock; 
} 
+0

你回答你自己的問題? – epicdev 2013-04-09 16:42:37

+9

我發佈了我遇到的問題的解決方案。由於我沒有博客,我把它放在這裏。我想這是因爲有一個複選框,當你張貼問題:) – Dan 2013-04-09 17:33:24

+1

你說得對,@丹做如此使用的一種有效方式。 – Travesty3 2013-07-23 20:31:07

5

- 不能被關閉在PHP> 5.3)已建成在實現Iterable陣列封裝:SPL Iterators。例如

$mock_iterator = new \ArrayIterator($items); 
$test_class->methodExpectingGenericIterator($mock_iterator); 
$resulting_data = $mock_iterator->getArrayCopy(); 
2

下面是它結合了兩者的優點,使用ArrayIterator內部的解決方案:

/** 
* @param array $items 
* 
* @return \PHPUnit_Framework_MockObject_MockObject|SomeIterator 
*/ 
private function createSomeIteratorMock(array $items = []) 
{ 
    $someIterator = $this->createMock(SomeIterator::class)->getMock(); 

    $iterator = new \ArrayIterator($items); 

    $someIterator 
     ->expects($this->any()) 
     ->method('rewind') 
     ->willReturnCallback(function() use ($iterator) { 
      $iterator->rewind(); 
     }) 
    ; 

    $someIterator 
     ->expects($this->any()) 
     ->method('current') 
     ->willReturnCallback(function() use ($iterator) { 
      return $iterator->current(); 
     }) 
    ; 

    $someIterator 
     ->expects($this->any()) 
     ->method('key') 
     ->willReturnCallback(function() use ($iterator) { 
      return $iterator->key(); 

     }) 
    ; 

    $someIterator 
     ->expects($this->any()) 
     ->method('next') 
     ->willReturnCallback(function() use ($iterator) { 
      $iterator->next(); 
     }) 
    ; 

    $someIterator 
     ->expects($this->any()) 
     ->method('valid') 
     ->willReturnCallback(function() use ($iterator) { 
      return $iterator->valid(); 
     }) 
    ; 

    return $someIterator; 
}