2011-01-31 27 views
6

Tôi có một phương pháp sau đây, mà lấy đầu truy cập các trang từ Google Analytics:Làm thế nào để đơn vị cuộc gọi thử nghiệm để Google API

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; 
} 

Nó cần một số refactoring cho chắc chắn, nhưng câu hỏi là:

  • Bạn sẽ viết các bài kiểm tra PHPUnit cho phương pháp này như thế nào?
+0

Bạn sẽ cần thử kết quả do google trả lại. Tôi nghĩ rằng một cách sẽ được đặt tất cả mọi thứ có liên quan đến việc nhận dữ liệu ($ kết quả) từ google đến một chức năng mới. Trong getData của bạn() nên chỉ có logic liên quan đến xử lý dữ liệu từ google. Trong phpUnit, bạn sẽ giả lập dữ liệu được trả về bởi google. – Marcin

Trả lời

2

David Weinraub đã cung cấp cho bạn nửa đầu (cách thiết lập lớp học của bạn để có thể nhại), vì vậy tôi sẽ giải quyết phần thứ hai (cách xây dựng mô hình).

PHPUnit cung cấp cơ sở nhạo báng tuyệt vời với API đơn giản. Việc chuyển người dùng và mật khẩu quá đơn giản để kiểm tra trong cuốn sách của tôi, vì vậy tôi chỉ giả sử việc xử lý truy vấn và kết quả. Điều này đòi hỏi mocks cho Zend_Gdata và 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]); 
} 

Ở trên sẽ kiểm tra xem URL có đúng hay không và trả lại nguồn cấp dữ liệu XML dự kiến ​​từ Google. Nó loại bỏ tất cả sự phụ thuộc vào các lớp Zend_Gdata. Nếu bạn không sử dụng kiểu gợi ý trên setClient(), bạn thậm chí có thể sử dụng stdClass làm cơ sở cho hai mocks vì bạn sẽ chỉ sử dụng các phương thức giả.

+0

+1 Giải thích hay và ví dụ rõ ràng. Và thực sự trả lời câu hỏi mà tôi không thực hiện được. ;-) –

6

Như tôi đã hiểu, thông thường bạn sẽ muốn tiêm phụ thuộc (đối tượng khách hàng của Google) vào Hệ thống đang kiểm tra (SUT, lớp chứa phương thức getData()).

Tôi luôn thấy các chuyên gia sử dụng công cụ xây dựng - và tôi chắc chắn đó là cách tiếp cận tốt hơn vì nó xác định rõ ràng các phụ thuộc phải lên phía trước. Nhưng, để nói sự thật, tôi dường như không bao giờ có thể thiết kế các đồ vật của mình đủ tốt để luôn làm việc đó. Vì vậy, tôi cuối cùng làm với tiêm setter.

Something như thế này:

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; 
} 

Sau đó, trong đơn vị kiểm tra, bạn tạo một đối tượng $client như một mô hình của live $client của bạn, thiết lập những kỳ vọng, và sau đó tiêm nó vào SUT của bạn bằng cách sử dụng phương pháp setClient($client) miêu tả trên.

Xem ý tôi là gì?

+0

Làm cách nào để kiểm tra xem liệu nguồn cấp dữ liệu phân tích của Google có thay đổi định dạng của nguồn cấp dữ liệu của anh ấy không? Kiểm tra đơn vị sẽ luôn vượt qua. – Nekresh

+2

@Nekresh. Bất cứ khi nào tôi gặp khó khăn quyết định làm thế nào để kiểm tra một cái gì đó tôi thường cố gắng để xem làm thế nào các xét nghiệm ZFs trông như thế nào và làm thế nào ZF người sáng tạo kiểm tra mã của họ. Bạn có thể học được rất nhiều chỉ bằng cách xem xét các bài kiểm tra của họ. Ví dụ, đối với Zend_Gdata (http://goo.gl/PKEsv), chúng thường có hai loại kiểm tra, kiểm tra trực tuyến và kiểm tra ngoại tuyến. – Marcin

+0

@Marcin: +1 Liên kết tuyệt vời. Cảm ơn bạn đã cứu. ;-) –

-1

Độ nghiêng đầu tiên của tôi là cho bạn biết rằng chức năng này getData là một trong những đoạn mã khó chịu nhất và xấu nhất. Bạn đang hỏi làm thế nào để kiểm tra đơn vị này. Tôi đoán đề xuất của tôi sẽ là gì? Cấu trúc lại.

Để cấu trúc lại mã này, bạn sẽ cần kiểm tra mức độ phù hợp .

Những lý do cho refactoring rất nhiều:

  1. Sự phụ thuộc vào khuôn khổ của bên thứ ba.
  2. Phụ thuộc vào dịch vụ bên ngoài.
  3. getData có quá nhiều trách nhiệm.

    a. Đăng nhập vào dịch vụ bên ngoài bằng cách sử dụng khung bên thứ ba.

    b. Tạo truy vấn cho dịch vụ bên ngoài.

    c. Phân tích truy vấn phân tích cú pháp từ dịch vụ bên ngoài.

Bạn đã tách mã của mình ra sao khỏi các thay đổi đối với khuôn khổ của bên thứ ba và từ dịch vụ bên ngoài?

Bạn thực sự nên xem cuốn sách của Michael Feather.Working Effectively with Legacy Code

[EDIT]

Quan điểm của tôi đối với bạn (spoiler sắp tới), là với mã này bạn không bao giờ có thể có được một đơn vị kiểm tra sự thật. Đó là vì sự phụ thuộc vào dịch vụ bên ngoài. Kiểm tra đơn vị không kiểm soát được dịch vụ hoặc dữ liệu mà nó trả về. Một bài kiểm tra đơn vị sẽ có thể thực hiện như vậy mà mỗi khi nó thực hiện kết quả của nó là nhất quán. Với dịch vụ bên ngoài, điều này có thể không phải như vậy. QUÝ VỊ KHÔNG CÓ KIỂM SOÁT VỀ DỊCH VỤ TRẢ LẠI BÊN NGOÀI.

Bạn sẽ làm gì nếu dịch vụ ngừng hoạt động? Kiểm tra đơn vị FAIL.

Điều gì sẽ xảy ra nếu kết quả được trả về? Kiểm tra đơn vị FAIL.

Kết quả kiểm tra đơn vị phải duy trì nhất quán từ thực thi đến thực thi. Nếu không, nó không phải là một thử nghiệm đơn vị.

+0

Cảm ơn, nhưng như tôi đã nêu trong câu hỏi, nó không phải là về tái cấu trúc, mà là về cách giả lập API. (Tôi không quen với mocks, stubs và mocking frameworks). Mã được tái cấu trúc sẽ quá dài để đăng ở đây. Phụ thuộc tiêm và loại bỏ các giá trị hardcoded là rõ ràng ở đây. – takeshin

Các vấn đề liên quan