symfony:如何根据请求的URL模拟对Guzzle Api请求的响应

时间:2015-11-05 15:45:18

标签: php unit-testing symfony mocking

我正在研究一个symfony 2项目。我有一个类,它根据输入值创建一个URL并启动对外部系统的请求。处理响应并返回处理结果。

对于本课程,功能测试正在进行中。在这种情况下,这意味着我正在运行测试,它真正调用外部服务并处理真正的答案。 现在我想添加真正的单元测试,所以我必须模拟我的请求和结果。对于我使用Guzzle Http客户端的请求。 我想要实现的是,如果我的方法只是调用url“http://example.com/certain/parameters”,那么我希望答案是“baa”。通过这个我想测试请求URL是否正确构建,并正确处理结果响应。

然而,我完全陷入了如何进行嘲弄的困境。

测试运行一个调用私有方法的公共方法。这是我的类'私有方法的一部分,其中包含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”方法获取响应,这是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()

任何人都有提示如何实现这一目标?它甚至可能吗?

1 个答案:

答案 0 :(得分:1)

francisco-spaeth的帮助下,我解决了这个问题:

我们就是这样做的:

在测试类中添加了三个私有属性:

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方法。

通过这种方式,模拟的行为应该如下:如果我的测试类使用了匹配的值,它只会返回所需的值。