【问题标题】:How to test a method which takes a callable as argument with PhpUnit and Prophecy如何使用 PhpUnit 和 Prophecy 测试将可调用对象作为参数的方法
【发布时间】:2016-09-16 20:42:13
【问题描述】:

我正在使用 Guzzle 的异步请求,并将它们实现在我现在想测试的服务中。

我的方法是这样的(假的,所以如果不是100%有效,请见谅)

public function getPlayer(string $uiid, array &$player = [])
{
    $options['query'] = ['id' => $uiid];

    $promise = $this->requestAsync('GET', $this->endpoint, $options);
    $promise->then(function (ResponseInterface $response) use (&$player) {
        $player = $response->getBody()->getContents();
    });

    return $players;
}

现在我想测试它,但我真的不知道如何模拟可调用对象,因为我总是收到错误

1) tzfrs\PlayerBundle\Tests\Api\Player\PlayerServiceTest::testGetPlayer Prophecy\Exception\InvalidArgumentException: Expected callable or instance of PromiseInterface, but got object.

这就是我目前的实现方式

/** @var ObjectProphecy|PromiseInterface $response */
$promise = $this->prophesize(PromiseInterface::class);

$promise->then()->will($this->returnCallback(function (ResponseInterface $response) use (&$player){}));

没用。还有这个

$this->returnCallback(function (ResponseInterface $response) use (&$player){})

也没有用。同样的错误。当只是尝试一个虚拟回调时

$promise->then(function(){});

我得到了错误Error: Call to a member function then() on string,即使在->reveal()首先承诺之后也是如此。有什么想法吗?

【问题讨论】:

    标签: php promise phpunit guzzle prophecy


    【解决方案1】:

    我有另一个想法。

    在requestAsync() 中创建一个可以生成你现在所做的东西的依赖项; 然后创建它的模拟,它将返回另一个承诺模拟。

    class PromiseMock
    {
        private $response;
    
        public function __construct(ResponseInterface $response)
        {
            $this->response = $response;
        }
    
        public function then($callable)
        {
            $callable($this->response);
        }
    }
    

    测试看起来像

    public function testGetPlayer()
    {
        $response = new Response(200, [], "Your test response");
        $promiseMock = new PromiseMock($response);
    
        $mockDependency = $this->getMockBuilder('YourDependencyClass')
                    ->getMock()
                    ->expects("requestAsync")->willReturn($promiseMock);
    
        $service = new YouServiceClass($mockDependency);
    
        $service->getPlayer("76245914-d56d-4bac-8419-9e409f43e777");
    }
    

    而且只在你的班级发生变化

    $promise = $this->someNameService->requestAsync('GET', $this->endpoint, $options);
    

    【讨论】:

    • 嘿。感谢您的回答。我更喜欢这个,但它仍然会过多地修改代码,并且then 函数似乎只是为了使测试工作的polyfill。 :/。有没有办法直接模拟一个可调用而不创建另一个类?
    • 但这就是 mocks 所做的,一般来说。而且我认为没有其他方法。
    • 对不起,我是个白痴。我没看到它是PromiseMock。我以为它仍然是Processor。我试试看
    • 嘿。对不起,我浪费了您的时间,因为您无法解决我的问题。我的错误隐藏在我没有在这里显示给你的一行中......我在测试中调用requestAsync 的客户有willReturn(PromiseInterface::class)。所以它是一个字符串。因此错误消息是正确的。我修好了...对不起!
    • 好的,没问题。
    【解决方案2】:

    我会为你的类注入一个处理器并称之为可调用的。看看吧,剩下的就很明显了:

    public function __construct(Processor $processor) {
        $this->processor = $processor;
    }
    
    public function getPlayer(string $uiid, array &$player = [])
    {
        $options['query'] = ['id' => $uiid];
    
        $promise = $this->requestAsync('GET', $this->endpoint, $options);
        $promise->then([$this->processor, "processResponse"]);
    
        $player = $this->processor->getPlayer();
    
        return $players;
    }
    

    和处理器:

    class Processor {
    
        private $player;        
    
        public function processResponse (ResponseInterface $response) {
            $this->player = $response->getBody()->getContents();
        }
    
        public function getPlayer() { return $this->player;}
    }
    

    【讨论】:

    • 这看起来我必须创建一个新类来测试我的服务,对吧?我不想那样做,改变我的项目只是为了让测试起作用。难道没有其他方法可以以某种方式模拟可调用对象吗?
    • 不,不会。它将创建一个遵循 SOLID 原则(这是 Lavarel 的关键原则之一)的新类 - 单一职责。您当前的实现假设您通过一个类创建两个函数 - 发送和解析响应。作为使用 SOLID 的好处之一,您将获得可测试的代码。
    • 有效点。虽然不使用 Laravel,但使用 Symfony。另一件事是,我已经将阅读和写作分开了。阅读发生在其他地方,但我认为我应该把它放在一起,因为它并没有真正改变 stackoverflow 问题的功能。但是,如果我像你一样实现它,通过数组而不是可调用方法调用方法,那将如何影响/改变我的测试?我仍然需要模拟 then 函数,我必须在其中传递一个回调 - 编辑:我将不得不去 aprox。 1小时。所以如果你回答,我会稍后回答
    • 只是为了澄清是否存在误解:我不是要求改进代码,而是要求如何使用 Prophecy 模拟 ->then() 方法。
    • 显然,我们同时吃午饭。在午餐时间,我有了另一个想法。仍然需要一些重构,但也许更接近你想要得到的东西
    猜你喜欢
    • 2018-05-28
    • 1970-01-01
    • 2015-03-20
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    相关资源
    最近更新 更多