9
votes

I have a class with the following function :

public function get(string $uri) : stdClass
{
    $this->client = new Client;
    $response = $this->client->request(
        'GET',
        $uri,
        $this->headers
    );

    return json_decode($response->getBody());
}

How can I mock the request method from PHPUnit? I tried different ways but it always tries to connect to the uri specified.

I tried with :

    $clientMock = $this->getMockBuilder('GuzzleHttp\Client')
        ->setMethods('request')
        ->getMock();

    $clientMock->expects($this->once())
        ->method('request')
        ->willReturn('{}');

But this didn't work. What can I do? I just need to mock the response to be empty.

Thanks

PD : Client comes from (use GuzzleHttp\Client)

3
how is $this->client initialized and how do you actually pass the mock?apokryfos
Don't you need to return an object implementing ResponseInterface instead of a string? Otherwise it'll blow up when you call getBody.iainn
sure, your mock should return a ResponseInterface stub so that getBody works (you will have to mock this also)Pierre
Also docs.guzzlephp.org/en/stable/testing.html might be better instead of the PHPUnit mocksapokryfos
I wanted to use phpunit mocksthemazz

3 Answers

15
votes

I think as suggested is better to use http://docs.guzzlephp.org/en/stable/testing.html#mock-handler

as it looks like the most elegant way to do it properly.

Thank you all

1
votes

The mocked Response doesn't need to be anything in particular, your code just expects it to be an object with a getBody method. So you can just use a stdClass, with a getBody method which returns some json_encoded object. Something like:

$jsonObject = json_encode(['foo']);
$uri = 'path/to/foo/bar/';

$mockResponse = $this->getMockBuilder(\stdClass::class)->getMock();

$mockResponse->method('getBody')->willReturn($jsonObject);

$clientMock = $this->getMockBuilder('GuzzleHttp\Client')->getMock();

$clientMock->expects($this->once())
    ->method('request')
    ->with(
        'GET', 
        $uri,
        $this->anything()
    )
    ->willReturn($mockResponse);

$result = $yourClass->get($uri);

$expected = json_decode($jsonObject);

$this->assertSame($expected, $result);
1
votes

I prefer this way to mock a Client in PHP. In this example I am using Guzzle Client.

Clone the code or install it via composer

$ composer require doppiogancio/mocked-client

And then...

$builder = new HandlerStackBuilder();

// Add a route with a response via callback
$builder->addRoute(
    'GET', '/country/IT', static function (ServerRequestInterface $request): Response {
        return new Response(200, [], '{"id":"+39","code":"IT","name":"Italy"}');
    }
);

// Add a route with a response in a text file
$builder->addRouteWithFile('GET',  '/country/IT/json', __DIR__ . '/fixtures/country.json');

// Add a route with a response in a string
$builder->addRouteWithFile('GET',  '{"id":"+39","code":"IT","name":"Italy"}');

// Add a route mocking directly the response
$builder->addRouteWithResponse('GET', '/admin/dashboard', new Response(401));

$client = new Client(['handler' => $builder->build()]);

Once you have mocked the client you can use it like this:

$response = $client->request('GET', '/country/DE/json');
$body = (string) $response->getBody();
$country = json_decode($body, true);

print_r($country);

// will return
Array
(
    [id] => +49
    [code] => DE
    [name] => Germany
)