PHPUnit 中的模拟与存根

问题描述 投票:0回答:3

我知道存根验证状态,模拟验证行为。

如何在 PHPUnit 中进行模拟以验证方法的行为? PHPUnit 没有验证方法 (verify()),而且我不知道如何在 PHPUnit 中进行模拟。

在文档中,创建存根有很好的解释:

// Create a stub for the SomeClass class.
$stub = $this->createMock(SomeClass::class);

// Configure the stub.
$stub
    ->method('doSomething')
    ->willReturn('foo');

// Calling $stub->doSomething() will now return 'foo'.
$this->assertEquals('foo', $stub->doSomething());

但在这种情况下,我正在验证状态,说返回答案。

如何创建模拟和验证行为的示例?

unit-testing testing mocking phpunit stub
3个回答
92
投票

PHPUnit 过去支持两种开箱即用的创建测试替身的方法。除了遗留的 PHPUnit 模拟框架,我们还可以选择 prophecy。

Prophecy 支持在 PHPUnit 9 中被删除,但可以通过安装

phpspec/prophecy-phpunit
来添加回来。

PHPUnit 模拟框架

createMock
方法用于创建三个最知名的测试替身。这是您配置对象的方式,使它成为虚拟对象、存根或模拟对象。

您还可以使用模拟构建器创建测试存根(

getMockBuilder
返回模拟构建器)。这只是做同样事情的另一种方式,让您可以通过流畅的界面调整一些额外的模拟选项(有关更多信息,请参阅文档)。

假人

Dummy 被传递,但从未真正被调用,或者如果它被调用,它会以默认答案响应(主要是

null
)。它的存在主要是为了满足参数列表。

$dummy = $this->createMock(SomeClass::class);

// SUT - System Under Test
$sut->action($dummy);

存根

存根与类似查询的方法一起使用 - 返回事物的方法,但它们是否被实际调用并不重要。

$stub = $this->createMock(SomeClass::class);
$stub->method('getSomething')
    ->willReturn('foo');

$sut->action($stub);

模拟

模拟与类似命令的方法一起使用——调用它们很重要,我们不太关心它们的返回值(命令方法通常不返回任何值)。

$mock = $this->createMock(SomeClass::class);
$mock->expects($this->once())
    ->method('doSomething')
    ->with('bar');

$sut->action($mock);

期望会在你的测试方法执行完成后自动验证。在上面的例子中,如果方法

doSomething
没有在
SomeClass
上调用,或者调用时使用的参数与您配置的参数不同,则测试将失败。

间谍

不支持。

预言

Prophecy 现在由 PHPUnit 开箱即用地支持,因此您可以将它用作遗留模拟框架的替代品。同样,这是您配置对象的方式使它成为特定类型的测试替身。

假人

$dummy = $this->prophesize(SomeClass::class);

$sut->action($dummy->reveal());

存根

$stub = $this->prophesize(SomeClass::class);
$stub->getSomething()->willReturn('foo');

$sut->action($stub->reveal());

模拟

$mock = $this->prophesize(SomeClass::class);
$mock->doSomething('bar')->shouldBeCalled();

$sut->action($mock->reveal());

间谍

$spy = $this->prophesize(SomeClass::class);

// execute the action on system under test
$sut->action($spy->reveal());

// verify expectations after 
$spy->doSomething('bar')->shouldHaveBeenCalled();

5
投票

傻瓜

首先,看假人。一个虚拟对象既是我的样子,如果你让我记住我把车钥匙放在哪里......也是你得到的对象,如果你在 phpspec 中添加一个带有类型提示的参数来获得测试替身......然后绝对不对它做任何事情。因此,如果我们得到一个测试替身并且不添加任何行为并且不对其方法进行断言,它就被称为“虚拟对象”。

哦,在他们的文档中,您会看到 $prophecy->reveal() 之类的东西。这是一个我们不需要担心的细节,因为 phpspec 会为我们处理好。得分!

存根

一旦你开始控制甚至一种方法的一个返回值……砰!这个对象突然被称为存根。来自文档:“存根是一个对象替身”——所有这些东西都被称为测试替身,或对象替身——当放置在特定环境中时,它们会以特定方式表现。这是一种奇特的说法:只要我们添加其中一个 willReturn() 东西,它就会变成一个存根。

实际上,大部分文档都在讨论存根和准确控制其行为方式的不同方法,包括我们之前看到的 Argument 通配符。

嘲笑

如果你继续往下读,接下来你会发现“mocks”。当你调用 shouldBeCalled() 时,一个对象变成了一个 mock。因此,如果您想添加一个方法被调用一定次数的断言,并且您想将该断言放在实际代码之前 - 使用 shouldBeCalledTimes() 或 shouldBeCalled() - 恭喜!你的对象现在被称为模拟。

间谍

最后,在底部,我们有间谍。间谍与模拟完全相同,只是当您在代码后添加期望时 - 就像 shouldHaveBeenCalledTimes() 一样。

https://symfonycasts.com/screencast/phpspec/doubles-dummies-mocks-spies


0
投票

简而言之,我们可以说:

您可以使用模拟对象“作为观察点,用于验证 SUT 运行时的间接输出。通常,模拟对象还包括测试存根的功能,因为如果它尚未通过测试,它必须将值返回给 SUT,但重点是验证间接输出。因此,模拟对象不仅仅是一个测试存根加断言;它以一种根本不同的方式使用.

© www.soinside.com 2019 - 2024. All rights reserved.