2016-07-22 40 views
0

我正在運行CakePHP 2.8.X,並且正在嘗試爲模型函數編寫單元測試。模擬CakePHP中模型方法中的方法

我們打電話給模型Item,我試圖測試它的getStatus方法。

但是,該模型在getStatus方法內調用其find

因此,像這樣:

class Item extends Model 
{ 
    public function getStatus($id) { 
     // Calls our `$this->Item-find` method 
     $item = $this->find('first', [ 
     'fields' => ['status'], 
     'conditions' => ['Item.id' => $id] 
     ]); 

     $status = $item['status']; 

     $new_status = null; 

     // Some logic below sets `$new_status` based on `$status` 
     // ... 

     return $new_status; 
    } 
} 

邏輯設置「$new_status」是一個有點複雜,這就是爲什麼我想寫一些測試它。

但是,我不完全確定如何覆蓋Item::getStatus內的find呼叫。

通常當我需要模擬模型的功能,我使用$this->getMock加上method('find')->will($this->returnValue($val_here)),但我並不想完全模仿我Item因爲我想測試其實際getStatus功能。

也就是說,在我的測試功能,我將被調用:

// This doesn't work since `$this->Item->getStatus` calls out to 
// `$this->Item->find`, which my test suite doesn't know how to compute. 
$returned_status = $this->Item->getStatus($id); 
$this->assertEquals($expected_status, $returned_status); 

那麼,如何溝通,我真正Item模型我的測試中,它應該覆蓋其內部調用其find方法?

回答

1

我知道這必須是他人所面臨的問題模型的獨立,它原來的PHPUnit有一個非常簡單的方法來解決這個問題!

This tutorial本質上給了我答案。

我確實需要創建一個模擬,但只有在'find'傳球,因爲我想嘲笑的方法,PHPUnit的幫忙,留下所有其他方法在我的模型獨自覆蓋它們。

相關部分從上面的教程是:

傳遞方法的名稱數組您getMock第二個參數產生,其中的方法,你已經確定

  • 是否所有的存根模仿對象,
  • 在默認情況下都返回NULL,
  • 很容易克服的

儘管方法,你也沒查出

  • 是否所有的嘲笑,
  • 運行包含名爲重點煤礦)當方法中的實際代碼,
  • 不要讓你覆蓋返回值

含義,我可以把th在嘲笑模型,並直接從我的打電話給我getStatus方法。該方法將運行其真實的代碼,並且當它到達find()時,它只會返回我傳入$this->returnValue的任何內容。

我使用dataProvider來傳遞我想要find方法返回的結果,以及在我的assertEquals調用中測試的結果。

所以我的測試功能看起來像:

/** 
* @dataProvider provideGetItemStatus 
*/ 
public function testGetItemStatus($item, $status_to_test) { 
    // Only mock the `find` method, leave all other methods as is 
    $item_model = $this->getMock('Item', ['find']); 

    // Override our `find` method (should only be called once) 
    $item_model 
     ->expects($this->once()) 
     ->method('find') 
     ->will($this->returnValue($item)); 

    // Call `getStatus` from our mocked model. 
    // 
    // The key part here is I am only mocking the `find` method, 
    // so when I call `$item_model->getStatus` it is actually 
    // going to run the real `getStatus` code. The only method 
    // that will return an overridden value is `find`. 
    // 
    // NOTE: the param for `getStatus` doesn't matter since I only use it in my `find` call, which I'm overriding 
    $result = $item_model->getStatus('dummy_id'); 

    $this->assertEquals($status_to_test, $result); 
} 

public function provideGetItemStatus() { 
    return [ 
     [ 
      // $item 
      ['Item' => ['id' = 1, 'status' => 1, /* etc. */]], 

      // status_to_test 
      1 
     ], 

     // etc... 
    ]; 
} 
+1

您可以接受你自己的答案讓其他人知道你已經解決您的問題。幹得好,這對其他人很有用。 – vascowhite

0

模擬查找的一種方法可能是使用測試特定的子類。

你可以創建一個TestItem來擴展item和覆蓋find,所以它不會執行db調用。

另一種方式可以是封裝NEW_STATUS邏輯和單元測試它