如何对Google API的调用进行单元测试

tak*_*hin 6 phpunit unit-testing zend-framework mocking google-api

我有以下方法,可从Google Analytics检索热门访问过的网页:

public function getData($limit = 10)
{
    $ids = '12345';
    $dateFrom = '2011-01-01';
    $dateTo = date('Y-m-d');

    // Google Analytics credentials
    $mail = 'my_mail';
    $pass = 'my_pass';

    $clientLogin = Zend_Gdata_ClientLogin::getHttpClient($mail, $pass, "analytics");
    $client = new Zend_Gdata($clientLogin);

    $reportURL = 'https://www.google.com/analytics/feeds/data?';

    $params = array(
        'ids' => 'ga:' . $ids,
        'dimensions' => 'ga:pagePath,ga:pageTitle',
        'metrics' => 'ga:visitors',
        'sort' => '-ga:visitors',
        'start-date' => $dateFrom,
        'end-date' => $dateTo,
        'max-results' => $limit
    );

    $query = http_build_query($params, '');
    $reportURL .= $query;

    $results = $client->getFeed($reportURL);

    $xml = $results->getXML();
    Zend_Feed::lookupNamespace('default');
    $feed = new Zend_Feed_Atom(null, $xml);

    $top = array();
    foreach ($feed as $entry) {
        $page['visitors'] = (int) $entry->metric->getDOM()->getAttribute('value');
        $page['url'] = $entry->dimension[0]->getDOM()->getAttribute('value');
        $page['title'] = $entry->dimension[1]->getDOM()->getAttribute('value');
        $top[] = $page;
    }

    return $top;
}
Run Code Online (Sandbox Code Playgroud)

它肯定需要一些重构,但问题是:

  • 你会如何为这个方法编写PHPUnit测试?

Dav*_*aub 6

据我了解,通常您会希望将依赖项(Google客户端对象)注入受测系统(SUT,包含该getData()方法的类).

我总是看到专家使用构造函数注入 - 我确信它是一种更好的方法,因为它可以在前面清楚地识别依赖关系.但是,说实话,我似乎永远不能设计好我的物品,以便始终做到这一点.所以我最终做了setter注入.

像这样的东西:

public function getClient()
{
    if (null === $this->_client){
        // $mail and $pass are stored somewhere, right?
        $clientLogin = Zend_Gdata_ClientLogin::getHttpClient($mail, $pass, "analytics");
        $this->_client = new Zend_Gdata($clientLogin);
    }
    return $this->_client;
}

public function setClient($client)
{
    $this->_client = $client;
    return $this;
}
Run Code Online (Sandbox Code Playgroud)

然后在单元测试中,您创建一个$client对象作为您的实时模拟$client,设置期望,然后使用上述setClient($client)方法将其注入您的SUT .

明白了吗?

  • @Nekresh.每当我遇到麻烦决定如何测试时,我通常会试着看看ZF测试的样子以及ZF创建者如何测试他们的代码.通过查看他们的测试,您可以学到很多东西.例如,对于Zend_Gdata(http://goo.gl/PKEsv),它们通常有两种测试,即在线测试和离线测试. (2认同)

Dav*_*ess 2

David Weinraub 向您介绍了前半部分(如何将您的类设置为可模拟的),所以我将介绍后半部分(如何构建模拟)。

PHPUnit 通过简单的 API 提供了强大的模拟工具。在我的书中,传递用户和密码太简单了,无法进行测试,因此我只会模拟查询和结果的处理。这需要 Zend_Gdata 和 Zend_Gdata_App_Feed 的模拟。

public function testGetData() {
    // expected input to and output from mocks
    $url = 'https://www.google.com/analytics/feeds/data?ids=ga:12345...';
    $xml = <<<XML
<feed>
    ...
</feed>
XML;
    // setup the mocks and method expectations
    $client = $this->getMock('Zend_Gdata', array('getFeed'));
    $feed = $this->getMock('Zend_Gdata_App_Feed', array('getXML'));
    $client->expects($this->once())
           ->method('getFeed')
           ->with($url)
           ->will($this->returnValue($feed));
    $feed->expects($this->once())
         ->method('getXML')
         ->will($this->returnValue($xml));
    // create the report (SUT) and call the method being tested
    $report = new MyReport();
    $report->setClient($client);
    $top = $report->getData();
    // check the final output; mocks are verified automatically
    $this->assertEquals(10, count($top));
    $this->assertEquals(array(
            'visitors' => 123, 
            'url' => 'http://...', 
            'title' => 'My Home Page'
        ), $top[0]);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将测试 URL 是否正确并返回 Google 期望的 XML 提要。它消除了对 Zend_Gdata 类的所有依赖。如果您不在 setClient() 上使用类型提示,您甚至可以使用 stdClass 作为两个模拟的基础,因为您将只使用模拟方法。