PHPUnit 覆盖模拟"willReturn()"语句



当我调用method((时,PHPUnit mock出现问题->willReturn((,第二次它似乎保持了在setUp上定义的第一个值:

<?php
namespace TestApplicationUseCase;
use ApplicationUseCaseCreateFoo;
use ApplicationExceptionsRequiredValueException;
use DomainEntityFoo;
use PHPUnitFrameworkTestCase;
class CreateFooTest extends TestCase
{
private CreateFoo $sut;
private Foo $foo_spy;
public function setUp()
{
$this->foo_spy = $this->createMock(Foo::class);
$this->foo_spy->method('getBar')->willReturn('bar value');
$this->foo_spy->method('getBaz')->willReturn('baz value');
$this->sut = new CreateFoo;
}
public function test_assert_given_foo_without_bar_throws_exception()
{
// Arrange
$this->expectException(RequiredValueException::class);
$this->expectExceptionMessage('Missing Bar value.');
$this->expectExceptionCode(500);
$this->foo_spy->method('getBar')->willReturn(null);
// var_dump($this->foo_spy->getBar());
// outputs: string(bar value)
// expected: null

// Act, Assert
$this->sut->execute($foo_spy);
}
public function test_assert_given_foo_without_baz_throws_exception()
{
// Arrange
$this->expectException(RequiredValueException::class);
$this->expectExceptionMessage('Missing Baz value.');
$this->expectExceptionCode(500);
$this->foo_spy->method('getBaz')->willReturn(null);
// var_dump($this->foo_spy->getBaz());
// outputs: string(baz value)
// expected: null
// Act, Assert
$this->sut->execute($foo_spy);
}
}

在不定义setUp的情况下,我必须重写每个测试的默认值,以便测试一个方法调用,如下所示:

$this->foo_spy->method('getBar0')->willReturn('bar value 0');
$this->foo_spy->method('getBar1')->willReturn('bar value 1');
$this->foo_spy->method('getBar2')->willReturn('bar value 2');
$this->foo_spy->method('getBar3')->willReturn('bar value 3');
$this->foo_spy->method('getBaz')->willReturn(null);

问题是:有时我有10个或更多的属性要测试,这会导致巨大的不必要的代码重复,所以我只想写一次默认的spy-mock,然后只在必要时修改,但正如你所看到的,当我尝试"重置";方法行为,它没有按预期工作。

看起来每次使用method时,都会创建一个新的匹配器
由于它们都将匹配,因此它将始终选择第一个。

在类似的情况下,我的解决方案是使用回调:

public function setUp()
{
$this->bar_value = 'bar value';
$this->baz_value = 'baz value';
$this->foo_spy = $this->createMock(Foo::class);
$this->foo_spy->method('getBar')->will( $this->returnCallback(function () { return $this->bar_value; } ) );
$this->foo_spy->method('getBaz')->will( $this->returnCallback(function () { return $this->baz_value; } ) );
$this->sut = new CreateFoo;
}

现在,您可以在单个测试中覆盖$this->bar_value,从而获得所需的结果。

setUp()函数在每个测试*函数之前运行,也许setUpBeforeClass()就是您要查找的,它只对该文件中的所有测试运行一次。

为了减少代码重复,您可以创建一个助手函数来获得mock:

public function getFooMock($barReturn = 'bar value', $bazReturn = 'baz value') {
$mock = $this->createMock(Foo::class);
$mock->method('getBar')->willReturn($barReturn);
$mock->method('getBaz')->willReturn($bazReturn);
return $mock;
}

然后像这样使用:

$this->sut->execute($this->getFooMock('bar value 0', null));

这应该会产生预期的结果。

最新更新