PHPUnit:如何模拟具有多个参数且没有直接顺序的多个方法调用?

Ole*_*ler 6 phpunit unit-testing zend-framework2

我需要测试以下功能:

[...]
public function createService(ServiceLocatorInterface $serviceManager)
{

    $firstService = $serviceManager->get('FirstServiceKey');
    $secondService = $serviceManager->get('SecondServiceKey');

    return new SnazzyService($firstService, $secondService);
}
[...]
Run Code Online (Sandbox Code Playgroud)

我知道,我可以这样测试:

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();
    }
    [...]
Run Code Online (Sandbox Code Playgroud)

或者

[...]
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();
}
[...]
Run Code Online (Sandbox Code Playgroud)

两者都可以正常工作,但是如果我在 createService 函数中交换 ->get(xxx) 行,则两个测试都将失败。那么,我该如何更改测试用例,它不需要参数“FirstServiceKey”、“SecondServiceKey”、...的特定序列。

Mat*_*teo 5

您可以尝试使用willReturnCallbackwillReturnMap策略,例如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();
}
Run Code Online (Sandbox Code Playgroud)

希望这有帮助