PHPUnit 覆盖 mock "willReturn()" 语句
PHPUnit override mock "willReturn()" statement
我在使用 PHPUnit 模拟时遇到问题,当我第二次调用 method()->willReturn() 时,它似乎保持了在 setUp 上定义的第一个值:
<?php
namespace Test\Application\UseCase;
use Application\UseCase\CreateFoo;
use Application\Exceptions\RequiredValueException;
use Domain\Entity\Foo;
use PHPUnit\Framework\TestCase;
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个或更多的属性要测试,这会导致大量不必要的代码重复,所以我想只写一次默认的间谍模拟,然后只在必要时修改,但正如你可以看到当我尝试“重置”方法行为时,它没有按预期工作。
setUp()
函数在每个 test* 函数之前 运行,也许 setUpBeforeClass()
就是你要找的东西,那只是 运行 一次在该文件中进行测试。
为了减少代码重复,您可以创建一个辅助函数来获取模拟:
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));
这应该会产生预期的结果。
看起来每次你使用method
,它都会创建一个新的匹配器。
由于所有这些都会匹配,因此它总是 select 第一个。
我在类似情况下的解决方案是使用回调:
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
,获得您想要的结果。
我在使用 PHPUnit 模拟时遇到问题,当我第二次调用 method()->willReturn() 时,它似乎保持了在 setUp 上定义的第一个值:
<?php
namespace Test\Application\UseCase;
use Application\UseCase\CreateFoo;
use Application\Exceptions\RequiredValueException;
use Domain\Entity\Foo;
use PHPUnit\Framework\TestCase;
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个或更多的属性要测试,这会导致大量不必要的代码重复,所以我想只写一次默认的间谍模拟,然后只在必要时修改,但正如你可以看到当我尝试“重置”方法行为时,它没有按预期工作。
setUp()
函数在每个 test* 函数之前 运行,也许 setUpBeforeClass()
就是你要找的东西,那只是 运行 一次在该文件中进行测试。
为了减少代码重复,您可以创建一个辅助函数来获取模拟:
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));
这应该会产生预期的结果。
看起来每次你使用method
,它都会创建一个新的匹配器。
由于所有这些都会匹配,因此它总是 select 第一个。
我在类似情况下的解决方案是使用回调:
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
,获得您想要的结果。