PHPUnit:如何模拟具有多个参数且没有直接顺序的多个方法调用?
PHPUnit: how to mock multiple method calls with multiple arguments and no straight order?
我需要测试以下功能:
[...]
public function createService(ServiceLocatorInterface $serviceManager)
{
$firstService = $serviceManager->get('FirstServiceKey');
$secondService = $serviceManager->get('SecondServiceKey');
return new SnazzyService($firstService, $secondService);
}
[...]
我知道,我可以这样测试:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testReturnValue()
{
$firstServiceMock = $this->createMock(FirstServiceInterface::class);
$secondServiceMock = $this->createMock(SecondServiceInterface::class);
$serviceManagerMock = $this->createMock(ServiceLocatorInterface::class);
$serviceManagerMock->expects($this->at(0))
->method('get')
->with('FirstServiceKey')
->will($this->returnValue($firstService));
$serviceManagerMock->expects($this->at(1))
->method('get')
->with('SecondServiceKey')
->will($this->returnValue($secondServiceMock));
$serviceFactory = new ServiceFactory($serviceManagerMock);
$result = $serviceFactory->createService();
}
[...]
或
[...]
public function testReturnValue()
{
$firstServiceMock = $this->createMock(FirstServiceInterface::class);
$secondServiceMock = $this->createMock(SecondServiceInterface::class);
$serviceManagerMock = $this->createMock(ServiceLocatorInterface::class);
$serviceManagerMock->expects($this->any())
->method('get')
->withConsecutive(
['FirstServiceKey'],
['SecondServiceKey'],
)
->willReturnOnConsecutiveCalls(
$this->returnValue($firstService),
$this->returnValue($secondServiceMock)
);
$serviceFactory = new ServiceFactory($serviceManagerMock);
$result = $serviceFactory->createService();
}
[...]
两者都工作正常,但如果我在 createService 函数中交换 ->get(xxx) 行,则两个测试都会失败。
那么,我该如何更改不需要参数特定序列的测试用例 'FirstServiceKey', 'SecondServiceKey, ...
您可以尝试使用 willReturnCallback
或 willReturnMap
策略,例如 willReturnCallback
:
public function testReturnValue()
{
$firstServiceMock = $this->createMock(FirstServiceInterface::class);
$secondServiceMock = $this->createMock(SecondServiceInterface::class);
$serviceManagerMock = $this->createMock(ServiceLocatorInterface::class);
$serviceManagerMock->expects($this->any())
->method('get')
->willReturnCallback(
function ($key) use($firstServiceMock, $secondServiceMock) {
if ($key == 'FirstServiceKey') {
return $firstServiceMock;
}
if ($key == 'SecondServiceKey') {
return $secondServiceMock;
}
throw new \InvalidArgumentException; // or simply return;
}
);
$serviceFactory = new ServiceFactory($serviceManagerMock);
$result = $serviceFactory->createService();
}
希望对您有所帮助
已经有一段时间了,所以有一个新的选项可以解决这个问题:更改为 'Prophets'
public function testReturnValue()
{
$this->container = $this->prophesize(ServiceLocatorInterface::class);
$firstService = $this->prophesize(FirstServiceInterface::class);
$secondService = $this->prophesize(SecondServiceKey::class);
$this->container->get('FirstServiceKey')->willReturn(firstService);
// And if you like
$this->container->get('FirstServiceKey')->shouldBeCalled();
$this->container->get('SecondServiceKey')->willReturn(secondService);
[...]
$serviceFactory = new ServiceFactory();
/*
* Little error in my example. ServiceLocatorInterface is parameter of
* createService() and not the constructor ;) (ZF2/3)
*/
$result = $serviceFactory->createService(
$this->container->reveal()
);
[...]
}
现在,按什么顺序生成“$serviceManager->get([...]);”并不重要调用 \o/
我需要测试以下功能:
[...]
public function createService(ServiceLocatorInterface $serviceManager)
{
$firstService = $serviceManager->get('FirstServiceKey');
$secondService = $serviceManager->get('SecondServiceKey');
return new SnazzyService($firstService, $secondService);
}
[...]
我知道,我可以这样测试:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testReturnValue()
{
$firstServiceMock = $this->createMock(FirstServiceInterface::class);
$secondServiceMock = $this->createMock(SecondServiceInterface::class);
$serviceManagerMock = $this->createMock(ServiceLocatorInterface::class);
$serviceManagerMock->expects($this->at(0))
->method('get')
->with('FirstServiceKey')
->will($this->returnValue($firstService));
$serviceManagerMock->expects($this->at(1))
->method('get')
->with('SecondServiceKey')
->will($this->returnValue($secondServiceMock));
$serviceFactory = new ServiceFactory($serviceManagerMock);
$result = $serviceFactory->createService();
}
[...]
或
[...]
public function testReturnValue()
{
$firstServiceMock = $this->createMock(FirstServiceInterface::class);
$secondServiceMock = $this->createMock(SecondServiceInterface::class);
$serviceManagerMock = $this->createMock(ServiceLocatorInterface::class);
$serviceManagerMock->expects($this->any())
->method('get')
->withConsecutive(
['FirstServiceKey'],
['SecondServiceKey'],
)
->willReturnOnConsecutiveCalls(
$this->returnValue($firstService),
$this->returnValue($secondServiceMock)
);
$serviceFactory = new ServiceFactory($serviceManagerMock);
$result = $serviceFactory->createService();
}
[...]
两者都工作正常,但如果我在 createService 函数中交换 ->get(xxx) 行,则两个测试都会失败。 那么,我该如何更改不需要参数特定序列的测试用例 'FirstServiceKey', 'SecondServiceKey, ...
您可以尝试使用 willReturnCallback
或 willReturnMap
策略,例如 willReturnCallback
:
public function testReturnValue()
{
$firstServiceMock = $this->createMock(FirstServiceInterface::class);
$secondServiceMock = $this->createMock(SecondServiceInterface::class);
$serviceManagerMock = $this->createMock(ServiceLocatorInterface::class);
$serviceManagerMock->expects($this->any())
->method('get')
->willReturnCallback(
function ($key) use($firstServiceMock, $secondServiceMock) {
if ($key == 'FirstServiceKey') {
return $firstServiceMock;
}
if ($key == 'SecondServiceKey') {
return $secondServiceMock;
}
throw new \InvalidArgumentException; // or simply return;
}
);
$serviceFactory = new ServiceFactory($serviceManagerMock);
$result = $serviceFactory->createService();
}
希望对您有所帮助
已经有一段时间了,所以有一个新的选项可以解决这个问题:更改为 'Prophets'
public function testReturnValue()
{
$this->container = $this->prophesize(ServiceLocatorInterface::class);
$firstService = $this->prophesize(FirstServiceInterface::class);
$secondService = $this->prophesize(SecondServiceKey::class);
$this->container->get('FirstServiceKey')->willReturn(firstService);
// And if you like
$this->container->get('FirstServiceKey')->shouldBeCalled();
$this->container->get('SecondServiceKey')->willReturn(secondService);
[...]
$serviceFactory = new ServiceFactory();
/*
* Little error in my example. ServiceLocatorInterface is parameter of
* createService() and not the constructor ;) (ZF2/3)
*/
$result = $serviceFactory->createService(
$this->container->reveal()
);
[...]
}
现在,按什么顺序生成“$serviceManager->get([...]);”并不重要调用 \o/