symfony:如何根据请求的 url 模拟对 Guzzle Api 请求的响应
symfony: How to mock a response to an Guzzle Api request depending on the requested url
我正在做一个 symfony 2 项目。我有一个 class,它根据输入值创建一个 url 并启动对外部系统的请求。处理响应并返回处理结果。
为此 class 功能测试正在运行。在那种情况下,这意味着我正在 运行 测试,它真正调用外部服务并处理真正的答案。
现在我想添加真正的单元测试,所以我必须模拟我的请求和结果。对于请求,我使用的是 Guzzle Http 客户端。
我想要实现的是,如果我的方法正在调用 url“http://example.com/certain/parameters”,那么我希望得到答案 "baa"。通过这个,我想测试请求 url 是否正确构建以及结果响应是否正确处理。
但是我完全不知道如何进行模拟。
测试运行一个 public 方法,该方法调用一个私有方法。这是我的 class' 私有方法的一部分,其中包含 Guzzle:
/**
* Fetch the Preview Urls from Cache Server.
*
* @param string $sCacheUrl
* @param integer $iObjnbr
*
* @return mixed
*/
private function fetchPreviewUrls($sCacheUrl, $iObjnbr)
{
$this->client = $this->client->get($sCacheUrl);
$response = $this->client->send();
if ($response->getStatusCode() <> 200) {
$this->logger->error('Image cache server unreachable. Http status "' . $response->getStatusCode() . '"');
} else {
$this->logger->debug('new cache url is "' . $response->getBody(true) . '"');
}
$json = $response->getBody(true);
//echo $json;
$aPreviews = $this->processJson($json, $iObjnbr);
//var_dump($aPreviews);
$aTeaser = array_shift($aPreviews);
if (empty($aTeaser)) {
$aTeaser = $aPreviews;
}
//var_dump($aTeaser);
return $aTeaser['url'];
}
棘手的部分是url设置在$client的"get"方法中。然后"send"方法用于获取response,response是send方法返回的对象。
我想将 get 调用的输入值与 send 调用的结果连接起来。
我尝试了很多,但到目前为止没有任何效果。
其中一个无效示例是:
public function testGetPreviewUrlBigImage()
{
$this->mockLogger = $this->getMock('\Psr\Log\LoggerInterface');
// given
$url = 'http://i5.example.com/teaser.php?action=list&objnbr=60963198&size=320';
$json =
'{"60963198":{"0":{"url":"http:\/\/i1.example.com\/teaser\/320\/8\/60963198.jpeg","size":320,"type":""}}}';
$clientMethods = get_class_methods('\Guzzle\Http\Client');
$this->mockClient = $this->getMock('\Guzzle\Http\Client', $clientMethods);
$this->mockClient->expects($this->any())->method('get')->method('send')->will(
$this->returnCallback(
function ($argument) use ($json, $url) {
$response = new \Guzzle\Http\Message\Response(200, [], $json);
return ($argument == $url) ? $response : false;
}
)
);
$this->linkService = new DefaultPreviewImageLinkService($this->mockLogger, $this->mockClient);
// when
$previewUrl = $this->linkService->getPreviewUrl(60963198, '', DefaultPreviewImageLinkService::IMAGE_BIG);
// then
$this->assertStringEndsWith('.jpeg', $previewUrl);
$this->assertRegExp('/^http:\/\/i[0-9]{1}.*/', $previewUrl);
$this->assertRegExp('/.*320.*jpeg/', $previewUrl);
}
这会导致致命错误
PHP 致命错误:在 null 上调用成员函数 getPreviewUrl()
有人知道如何实现这一点吗?有可能吗?
在 francisco-spaeth 的帮助下,我解决了这个问题:
我们是这样做的:
测试中添加了三个私有属性class:
private $mockLogger;
private $mockClient;
private $linkService;
我添加了一个准备模拟的方法
public function prepareMocks($url, $json)
{
$responseInterface = $this->getMockBuilder('\Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$responseInterface->method('getBody')
->with($this->equalTo(true))
->will($this->returnValue($json));
$requestInterface = $this->getMock('\Guzzle\Http\Message\RequestInterface');
$requestInterface->method('send')->will($this->returnValue($responseInterface));
$this->mockClient = $this->getMock('\Guzzle\Http\Client');
$this->mockClient->method('get')
->with($this->equalTo($url))
->will($this->returnValue($requestInterface));
$this->linkService = new DefaultPreviewImageLinkService($this->mockLogger, $this->mockClient);
}
在测试方法中调用如下:
public function testGetPreviewUrlBigImage()
{
// Given:
$url = 'http://i1.example.com/teaser.php?action=list&objnbr=60963198&size=320';
$json = '{"60963198":{"0":{"url":"http:\/\/i1.example.com\/teaser\/320\/8\/60963198.jpeg",'
. '"size":320,"type":""}}}';
$this->prepareMocks($url, $json);
$class_methods = get_class_methods($this->linkService);
// When:
$previewUrl = $this->linkService->getPreviewUrl(60963198, '', DefaultPreviewImageLinkService::IMAGE_BIG);
// Then:
$this->assert.....
}
通过在一个方法中准备模拟,我保持代码干净,因为每次测试都必须调用它。
我只是将所需的输入值输入 prepareMock 方法。
通过这个模拟的行为就像它应该的那样:如果匹配值被我测试的class使用,它只会返回所需的值class。
我正在做一个 symfony 2 项目。我有一个 class,它根据输入值创建一个 url 并启动对外部系统的请求。处理响应并返回处理结果。
为此 class 功能测试正在运行。在那种情况下,这意味着我正在 运行 测试,它真正调用外部服务并处理真正的答案。 现在我想添加真正的单元测试,所以我必须模拟我的请求和结果。对于请求,我使用的是 Guzzle Http 客户端。 我想要实现的是,如果我的方法正在调用 url“http://example.com/certain/parameters”,那么我希望得到答案 "baa"。通过这个,我想测试请求 url 是否正确构建以及结果响应是否正确处理。
但是我完全不知道如何进行模拟。
测试运行一个 public 方法,该方法调用一个私有方法。这是我的 class' 私有方法的一部分,其中包含 Guzzle:
/**
* Fetch the Preview Urls from Cache Server.
*
* @param string $sCacheUrl
* @param integer $iObjnbr
*
* @return mixed
*/
private function fetchPreviewUrls($sCacheUrl, $iObjnbr)
{
$this->client = $this->client->get($sCacheUrl);
$response = $this->client->send();
if ($response->getStatusCode() <> 200) {
$this->logger->error('Image cache server unreachable. Http status "' . $response->getStatusCode() . '"');
} else {
$this->logger->debug('new cache url is "' . $response->getBody(true) . '"');
}
$json = $response->getBody(true);
//echo $json;
$aPreviews = $this->processJson($json, $iObjnbr);
//var_dump($aPreviews);
$aTeaser = array_shift($aPreviews);
if (empty($aTeaser)) {
$aTeaser = $aPreviews;
}
//var_dump($aTeaser);
return $aTeaser['url'];
}
棘手的部分是url设置在$client的"get"方法中。然后"send"方法用于获取response,response是send方法返回的对象。 我想将 get 调用的输入值与 send 调用的结果连接起来。
我尝试了很多,但到目前为止没有任何效果。
其中一个无效示例是:
public function testGetPreviewUrlBigImage()
{
$this->mockLogger = $this->getMock('\Psr\Log\LoggerInterface');
// given
$url = 'http://i5.example.com/teaser.php?action=list&objnbr=60963198&size=320';
$json =
'{"60963198":{"0":{"url":"http:\/\/i1.example.com\/teaser\/320\/8\/60963198.jpeg","size":320,"type":""}}}';
$clientMethods = get_class_methods('\Guzzle\Http\Client');
$this->mockClient = $this->getMock('\Guzzle\Http\Client', $clientMethods);
$this->mockClient->expects($this->any())->method('get')->method('send')->will(
$this->returnCallback(
function ($argument) use ($json, $url) {
$response = new \Guzzle\Http\Message\Response(200, [], $json);
return ($argument == $url) ? $response : false;
}
)
);
$this->linkService = new DefaultPreviewImageLinkService($this->mockLogger, $this->mockClient);
// when
$previewUrl = $this->linkService->getPreviewUrl(60963198, '', DefaultPreviewImageLinkService::IMAGE_BIG);
// then
$this->assertStringEndsWith('.jpeg', $previewUrl);
$this->assertRegExp('/^http:\/\/i[0-9]{1}.*/', $previewUrl);
$this->assertRegExp('/.*320.*jpeg/', $previewUrl);
}
这会导致致命错误 PHP 致命错误:在 null 上调用成员函数 getPreviewUrl()
有人知道如何实现这一点吗?有可能吗?
在 francisco-spaeth 的帮助下,我解决了这个问题:
我们是这样做的:
测试中添加了三个私有属性class:
private $mockLogger;
private $mockClient;
private $linkService;
我添加了一个准备模拟的方法
public function prepareMocks($url, $json)
{
$responseInterface = $this->getMockBuilder('\Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$responseInterface->method('getBody')
->with($this->equalTo(true))
->will($this->returnValue($json));
$requestInterface = $this->getMock('\Guzzle\Http\Message\RequestInterface');
$requestInterface->method('send')->will($this->returnValue($responseInterface));
$this->mockClient = $this->getMock('\Guzzle\Http\Client');
$this->mockClient->method('get')
->with($this->equalTo($url))
->will($this->returnValue($requestInterface));
$this->linkService = new DefaultPreviewImageLinkService($this->mockLogger, $this->mockClient);
}
在测试方法中调用如下:
public function testGetPreviewUrlBigImage()
{
// Given:
$url = 'http://i1.example.com/teaser.php?action=list&objnbr=60963198&size=320';
$json = '{"60963198":{"0":{"url":"http:\/\/i1.example.com\/teaser\/320\/8\/60963198.jpeg",'
. '"size":320,"type":""}}}';
$this->prepareMocks($url, $json);
$class_methods = get_class_methods($this->linkService);
// When:
$previewUrl = $this->linkService->getPreviewUrl(60963198, '', DefaultPreviewImageLinkService::IMAGE_BIG);
// Then:
$this->assert.....
}
通过在一个方法中准备模拟,我保持代码干净,因为每次测试都必须调用它。 我只是将所需的输入值输入 prepareMock 方法。
通过这个模拟的行为就像它应该的那样:如果匹配值被我测试的class使用,它只会返回所需的值class。