phpunit 测试 guzzle 响应是空对象

phpunit test guzzle response is empty object

我有以下测试:

public function it_can_find_by_id(): void
    {
        $oauth = factory( SuperOfficeOAuth::class )->create();
        Auth::login( $oauth->user );
        $content = file_get_contents( __DIR__.'/../_sample-responses/Contact/find.json' );

        Http::fake( [
            'superoffice.com/*' => Http::response( $content, 200, [ 'Content-Type' => 'application/json' ] ),
        ] );

        $contact = ( new Contact )->find(3);

        $this->assertIsObject( $contact );
        $this->assertEquals(3, $contact->contact_id);
    } 

find.json 文件:

{
    "ContactId": 3,
    "Name": "Amadeus AS",
    "Department": "AAvdeling",
    "OrgNr": "",
    "Number1": "AmadeusA",
    "Number2": "AA10011",
    "UpdatedDate": "2006-02-22T09:35:41",
    "CreatedDate": "2002-07-23T16:01:34",
    "Emails": [
        {
            "Value": "qa0@superoffice.com",
            "StrippedValue": "qa0@superoffice.com",
            "Description": "",
            "TableRight": null,
            "FieldProperties": {
            }
        }
    ]
}
class Contact extends Model
{
    public function find(int $id): object
    {
        $this->url .= '/'.$id;
        return parent::get();
    }
}

// There is another class handeling tokens but isn't relevant for the example

class Model extends Client
{ 
    public function get()
    {
        $this->setUrl();

        $this->setHeaders();

        return parent::get();
    }
}

// Client Class

use GuzzleHttp\HandlerStack;
use Psr\Http\Message\StreamInterface;
use Spatie\GuzzleRateLimiterMiddleware\RateLimiterMiddleware;
use GuzzleHttp\Client as GuzzleClient;

class Client
{


    public function __construct()
    {
        $stack = HandlerStack::create();
        $stack->push(RateLimiterMiddleWare::perSecond(10));
        $this->client = new GuzzleClient();
    }

    public function get()
    {
        return (object)json_decode($this->request('GET', $this->url, $this->headers)->getBody());
    }

    private function request(string $method, string $url, ?array $headers = null, ?array $body = null): object
    {
       $response = null;
        try
        {
            $response = $this->client->request($method, $url, [
                'headers' => $headers,
                'body' => json_encode($body)
            ] );
        }
        catch(ClientException $exception)
        {
        }
       
        return $response;
     }
}

因此,在测试和调试联系人 find 方法时,parent::get() returns {#742} 而不是 [=16= 中的 json 对象] 文件。所以我的问题是如何测试/模拟响应以使其 return 成为 json 对象?在生产中,它按预期 return json 对象。

还尝试模拟如下响应:

$this->mock(function($mock) use($content) {
            $mock->shouldReceive('request')->andReturns(new Response(200, [], $content));
});

$mock = Mockery::mock(Client::class);
$mock->shouldReceive('request')
     ->andReturn(new Response(200, [], $content));

$this->app->instance(Client::class, $mock);

class TestCase
{
    public function createMockResponse($content, int $status = 200): Response
    {
        $content = utf8_encode($content);
        $response = new Response($status, ['Content-Type' => 'application/json'], json_encode($content));

        $mock = new MockHandler([$response]);

        $handler = HandlerStack::create($mock);
        $client = new Client(['handler' => $handler]);

        $this->app->instance(Client::class, $client);

        return $response;
    }
}

摘自 here.

事实证明,经过测试的模拟的最后一个示例确实有效。它需要一些改变:

class TestCase
{
    public $client;

    public function createMockResponse($content, int $status = 200): Response
    {
        $content = utf8_encode($content);
        $response = new Response($status, ['Content-Type' => 'application/json'], json_encode($content));

        $mock = new MockHandler([$response]);

        $handler = HandlerStack::create($mock);
        $this->client = new Client(['handler' => $handler]);

        $this->app->instance(Client::class, $this->client);

        return $response;
    }
}

每个“扩展”class 都需要这样的结构:

public function __construct(?GuzzleClient $client = null)
{
    parent::__construct($client);
}

在我自己的 Client class 中,可以执行以下操作:

public function __construct(?GuzzleClient $client = null)
{
    $stack = HandlerStack::create();
    $stack->push(RateLimiterMiddleWare::perSecond(10));
    $this->client = $client ?? new GuzzleClient();
}

这样我的 Client 就可以使用模拟客户端了。

(new Contact($this->client))->find(3);

感觉这不是最佳做法,但目前它有效。