2017-01-01 18 views
0

我有這樣的情況下,我想運行PHPUnit的測試和檢查電流測試類的行爲如下:如何在PHPUnit中爲當前測試類做預言?

public function it_allows_to_add_items() 
{ 
     // Create prophesies 
     $managerProphecy = $this->getProphet(ListingManager::class); 
     $listingItemProphecy = $this->getProphet(ListingItemInterface::class); 

     $listing = factory(\App\Misc\Listings\Listing::class)->create(); 
     $manager = new ListingManager($listing); 

     $item = factory(\App\Misc\Listings\ListingItem::class)->make(['listing_id' => null]); 
     $item2 = factory(\App\Misc\Listings\ListingItem::class)->make(['listing_id' => null]); 

     $manager->addItem($item); 
     $managerProphecy->validate($listingItemProphecy)->shouldBeCalledTimes(2); 
     $manager->addItem($item2); 

     $this->assertTrue(true); 
    } 

的是,甚至有可能?

當然我越來越

1) GenericListingManagerTest::it_allows_to_add_items 
Some predictions failed: 
    Double\App\Misc\Listings\ListingManager\P2: 
    Expected exactly 2 calls that match: 
     Double\App\Misc\Listings\ListingManager\P2->validate(exact(Double\ListingItemInterface\P1:00000000058d2b7a00007feda4ff3b5f Object (
     'objectProphecy' => Prophecy\Prophecy\ObjectProphecy Object (*Prophecy*) 
    ))) 
    but none were made. 
+0

我想知道這是甚至可能的而不是相當不好的設計組件,如驗證程序,可以作爲預言提供列表管理器依賴項。 –

回答

1

我覺得你的方式來處理這個測試是有點過。如果我理解正確,則要驗證addItem(object $item)是否正常工作,即經理包含該項目,並且該項目與您添加的項目相同。爲此,你不應該需要預言,事實上你的測試實際上並沒有使用你所創造的預言。根據你的經理是什麼樣子,你可以寫這樣的事情:

function test_manager_add_items_stores_item_and_increases_count() 
{ 
    $manager = new ListingManager(); // (2) 
    $item = new ListingIem(); // (3) 
    $initialCount = $manager->countItems(); // (1) 

    $manager->addItem($item); 

    $this->assertEquals($initialCount + 1, $manager->countItems()); 
    // Assuming offset equals (item count - 1) just like in a numeric array 
    $this->assertSame($item, $manager->getItemAtOffset($initialCount)); 
} 

(1)假設你的經理已經計數() - 方法,你可以前後數是否增加了附加的項目數查詢。 (2)我們想測試真正的管理者 - 這就是爲什麼我們不需要這裏預言創建的模擬 - 我們可以使用一個真實的項目,因爲它只是一個價值。

(3)我不知道爲什麼你有一個ListingItemInterface。是否真的有不同的ListingItem實現,如果是這樣,你真的想要一個通用的ListingManager,可能包含所有這些,或者你需要一個更具體的ListingManager來確保每個管理器只包含它的特定類型的項目?這實際上取決於您的用例,但看起來您可能違反了SOLID中的I(接口隔離原則)或L(Liskov替換原則)。

根據您的用例,您可能需要添加實際項目,例如2種不同的類型來表明它可以將2種不同的接口實現放在那裏,或者你可以像上面那樣做,只需添加一個ListingItem並驗證管理器中的每個項目都實現了接口 - 我找到斷言對你來說;)。當然,您也可以使用您的工廠創建該項目。重要的是,我們使用assertSame()來測試被管理對象和我們最初創建的對象是否相同,這意味着引用完全相同的對象。

如果您想確保額外的行爲,例如限制您可以放入管理器中的項目類型,或者在將無效對象放入其中時的行爲方式,您可以添加其他測試。

重要的是,你想測試經理的實際行爲,這就是爲什麼你不想使用它的模擬。如果你真的需要它,你可以使用一個模擬的ListingItemInterface。在這種情況下,測試可能應該是這個樣子:

function test_manager_add_items_stores_item_and_increases_count() 
{ 
    $manager = new ListingManager(); 
    $dummyItem = $this->prophecy(ListingIemInterface::class); 
    $initialCount = $manager->countItems(); 

    $manager->addItem($dummyItem->reveal()); 

    $this->assertEquals($initialCount + 1, $manager->countItems()); 
    $this->assertSame($dummyItem, $manager->getItemAtOffset($initialCount)); 
} 

編輯:如果addItem檢查要跳過驗證,例如因爲你提供的空物品無效,你不在乎。您可以使用PHPUnit的嘲弄自己的框架,以部分地嘲笑經理是這樣的:

$item = new ListingItem(); 
$managerMock = $this->getMockBuilder(ListManager::class) 
    ->setMethods(['validate']) 
    ->getMock(); 
$managerMock 
    ->expects($this->exactly(2)) 
    ->method('validate') 
    ->with($this-> identicalTo($item)) 
    ->willReturn(true); 

$managerMock->addItem($item); 
$managerMock->addItem($item); 

您不必主張在年底什麼,因爲expects()已經斷言的東西。除了validate()之外,您的管理器將正常工作,這意味着您將在addItem()中運行代碼,並且如果在那裏調用驗證(每個項目一次),測試將通過。

+0

對於ListingItemInterface模擬的這種情況將不起作用,因爲我正在使用真正的數據庫連接(不是我正在寫的集成測試?)所以虛擬不會提供預期的數據設置。 其實我想知道是否可以像我想要的那樣使用模擬,因爲我想添加的另一個行爲測試是驗證($ item)檢查。所以使用PHPUnit我一直在嘲笑ListingManager以檢查是否運行了該方法(或引發異常等) –

+0

無論如何,我已經做了一步,並通過https://kahlan.github.io/構建斷言測試框架,它允許我檢查方法是否被觸發。 –

+0

我對你想要測試的東西有點困惑,這可能表明你的測試太複雜或者命名已關閉。你能否提供'addItem()'的源代碼?也許用你試圖測試的代碼樣本會更容易理解。如果你只是想確保兩個項目都通過驗證檢查(因爲它在'addItem()')中被調用),那麼你可能想看看部分模擬,這是預言不可能的。我會更新我的答案以包含一個例子。 – dbrumann