如何在PHPUnit中为当前测试类做预言?

时间:2017-01-01 11:04:09

标签: laravel phpunit prophecy

我有这种情况,我想运行PHPUnit测试并检查当前测试类的行为,如下所示:

28*28

甚至可能吗?

我当然得到了

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 个答案:

答案 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)假设你的经理有一个count() - 方法,你可以检查计数是否增加了计数。

(2)我们想测试真正的经理 - 这就是为什么我们不需要这里预言创建的模拟 - 我们可以使用真实项目,因为它只是一个值。

(3)我不确定你为什么有一个ListingItemInterface。是否真的有不同的ListingItem实现,如果是这样,你真的想要一个可能包含所有这些的通用的ListingManager,还是需要更具体的一个来确保每个Manager只包含它的特定项目?这实际上取决于您的用例,但看起来您可能违反了SOL中的I(接口隔离原则)或L(Liskov替换原则)。

根据您的使用情况,您可能希望添加实际商品,例如2种不同的类型,以明确你可以在那里放置2个不同的接口实现,或者你可以像上面那样做,只需添加一个ListingItem并验证管理器中的每个项目都实现了接口 - 我发现断言对你而言;)。当然,您也可以使用工厂来创建项目。重要的是我们用assertSame()测试托管对象和我们最初创建的对象是否相同,意味着引用完全相同的对象。

如果要确保其他行为,可以添加其他测试,例如限制可以放在管理器中的项目类型或者在将无效对象放入其中时的行为方式。

重要的是,您要测试Manager的实际行为,这就是您不想使用模拟的原因。如果你真的需要它,你可以使用mockItemInterface。在这种情况下,测试应该看起来像这样:

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()中运行代码,如果在那里调用validate(每个项目一次),测试将通过。