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);
感觉这不是最佳做法,但目前它有效。
我有以下测试:
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);
感觉这不是最佳做法,但目前它有效。