Musterknabe
Musterknabe

Reputation: 6081

How to test a method which takes a callable as argument with PhpUnit and Prophecy

I'm using Guzzle's asynchronous request and have them implemented in a service that I would like to test now.

My method looks like this (pseudo, so if it's not 100% valid, please excuse)

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;
}

Now I want to test it, but I don't really know how to mock the callable, because I'm always getting the error

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

This is how I have it implemented currently

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

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

Didn't work. And this

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

didn't work either. Same error. And when simply trying a dummy callback

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

I get the error Error: Call to a member function then() on string, even after ->reveal()ing the promise first. Any ideas?

Upvotes: 0

Views: 6763

Answers (2)

Dmitry Malyshenko
Dmitry Malyshenko

Reputation: 3051

I had another idea.

Make a dependency that will make what you make now in requestAsync(); And then create it's mock that will return another mock of promise.

class PromiseMock
{
    private $response;

    public function __construct(ResponseInterface $response)
    {
        $this->response = $response;
    }

    public function then($callable)
    {
        $callable($this->response);
    }
}

test looks like

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");
}

And in your class changes only

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

Upvotes: 2

Dmitry Malyshenko
Dmitry Malyshenko

Reputation: 3051

I would inject a processor to your class and call it's callable. Check it out, the rest is quite obvious:

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;
}

And processor:

class Processor {

    private $player;        

    public function processResponse (ResponseInterface $response) {
        $this->player = $response->getBody()->getContents();
    }

    public function getPlayer() { return $this->player;}
}

Upvotes: 1

Related Questions