Các bài kiểm tra tích hợp được viết như thế nào để tương tác với API bên ngoài?


79

Đầu tiên, kiến ​​thức của tôi ở đâu:

Unit Test là những bài kiểm tra một đoạn mã nhỏ (chủ yếu là các phương pháp đơn lẻ).

Các bài kiểm tra tích hợp là những bài kiểm tra sự tương tác giữa nhiều vùng mã (hy vọng đã có các bài kiểm tra đơn vị của riêng chúng). Đôi khi, các phần của mã đang được kiểm tra yêu cầu mã khác hoạt động theo một cách cụ thể. Đây là lúc Mocks & Stubs xuất hiện. Vì vậy, chúng tôi mô phỏng / trích xuất một phần của mã để thực hiện rất cụ thể. Điều này cho phép Kiểm tra tích hợp của chúng tôi chạy có thể dự đoán trước mà không có tác dụng phụ.

Tất cả các thử nghiệm sẽ có thể chạy độc lập mà không cần chia sẻ dữ liệu. Nếu việc chia sẻ dữ liệu là cần thiết, đây là dấu hiệu hệ thống chưa đủ tách rời.

Tiếp theo, tình huống tôi đang gặp phải:

Khi tương tác với một API bên ngoài (cụ thể là một API RESTful sẽ sửa đổi dữ liệu trực tiếp với một yêu cầu POST), tôi hiểu rằng chúng ta có thể (nên?) Mô phỏng tương tác với API đó (được nêu một cách hùng hồn hơn trong câu trả lời này ) để Kiểm tra tích hợp . Tôi cũng hiểu rằng chúng tôi có thể Unit Test từng thành phần tương tác với API đó (xây dựng yêu cầu, phân tích cú pháp kết quả, đưa ra lỗi, v.v.). Những gì tôi không nhận được là làm thế nào để thực sự đi về điều này.

Vì vậy, cuối cùng: (Các) câu hỏi của tôi.

Làm cách nào để kiểm tra sự tương tác của tôi với một API bên ngoài có tác dụng phụ?

Một ví dụ hoàn hảo là API nội dung của Google để mua sắm . Để có thể thực hiện nhiệm vụ trước mắt, nó đòi hỏi một lượng công việc chuẩn bị kha khá, sau đó thực hiện yêu cầu thực tế, sau đó phân tích giá trị trả về. Một số điều này không có bất kỳ môi trường 'hộp cát' nào .

Mã để làm điều này thường có khá nhiều lớp trừu tượng, giống như:

<?php
class Request
{
    public function setUrl(..){ /* ... */ }
    public function setData(..){ /* ... */ }
    public function setHeaders(..){ /* ... */ }
    public function execute(..){
        // Do some CURL request or some-such
    }   
    public function wasSuccessful(){
        // some test to see if the CURL request was successful
    }   
}

class GoogleAPIRequest
{
    private $request;
    abstract protected function getUrl();
    abstract protected function getData();

    public function __construct() {
        $this->request = new Request();
        $this->request->setUrl($this->getUrl());
        $this->request->setData($this->getData());
        $this->request->setHeaders($this->getHeaders());
    }   

    public function doRequest() {
        $this->request->execute();
    }   
    public function wasSuccessful() {
        return ($this->request->wasSuccessful() && $this->parseResult());
    }   
    private function parseResult() {
        // return false when result can't be parsed
    }   

    protected function getHeaders() {
        // return some GoogleAPI specific headers
    }   
}

class CreateSubAccountRequest extends GoogleAPIRequest
{
    private $dataObject;

    public function __construct($dataObject) {
        parent::__construct();
        $this->dataObject = $dataObject;
    }   
    protected function getUrl() {
        return "http://...";
    }
    protected function getData() {
        return $this->dataObject->getSomeValue();
    }
}

class aTest
{
    public function testTheRequest() {
        $dataObject = getSomeDataObject(..);
        $request = new CreateSubAccountRequest($dataObject);
        $request->doRequest();
        $this->assertTrue($request->wasSuccessful());
    }
}
?>

Lưu ý: Đây là một ví dụ PHP5 / PHPUnit

Cho rằng đó testTheRequestlà phương thức được gọi bởi bộ thử nghiệm, ví dụ sẽ thực hiện một yêu cầu trực tiếp.

Bây giờ, yêu cầu trực tiếp này sẽ (hy vọng, với điều kiện mọi thứ diễn ra tốt đẹp) thực hiện một yêu cầu ĐĂNG có tác dụng phụ là thay đổi dữ liệu trực tiếp.

điều này có chấp nhận được không? Tôi có những lựa chọn thay thế nào? Tôi không thể thấy cách nào để mô phỏng đối tượng Yêu cầu cho bài kiểm tra. Và ngay cả khi tôi đã làm vậy, điều đó có nghĩa là thiết lập kết quả / điểm nhập cho mọi đường dẫn mã có thể mà API của Google chấp nhận (trong trường hợp này sẽ phải được tìm thấy bằng cách thử và sai), nhưng sẽ cho phép tôi sử dụng đồ đạc.

Một phần mở rộng hơn nữa là khi một số yêu cầu nhất định dựa trên một số dữ liệu nhất định đang Trực tiếp. Sử dụng lại Google Content API làm ví dụ, để thêm Nguồn cấp dữ liệu vào Tài khoản phụ, thì Tài khoản phụ phải tồn tại.

Một cách tiếp cận mà tôi có thể nghĩ đến là các bước sau;

  1. Trong testCreateAccount
    1. Tạo một tài khoản phụ
    2. Khẳng định tài khoản phụ đã được tạo
    3. Xóa tài khoản phụ
  2. testCreateDataFeedphụ thuộc vào testCreateAccountkhông có bất kỳ lỗi
    1. Trong testCreateDataFeed, tạo một tài khoản mới
    2. Tạo nguồn cấp dữ liệu
    3. Khẳng định nguồn cấp dữ liệu đã được tạo
    4. Xóa nguồn cấp dữ liệu
    5. Xóa tài khoản phụ

Điều này sau đó đặt ra câu hỏi xa hơn; làm cách nào để kiểm tra việc xóa tài khoản / nguồn cấp dữ liệu? testCreateDataFeedTôi cảm thấy bẩn - Điều gì sẽ xảy ra nếu việc tạo nguồn cấp dữ liệu không thành công? Kiểm tra không thành công, do đó tài khoản phụ không bao giờ bị xóa ... Tôi không thể kiểm tra xóa mà không tạo, vì vậy tôi viết một kiểm tra khác ( testDeleteAccount) dựa vào testCreateAccounttrước khi tạo sau đó xóa tài khoản của chính nó (vì dữ liệu không nên được chia sẻ giữa các lần kiểm tra).

Tóm tắt

  • Làm cách nào để kiểm tra việc tương tác với một API bên ngoài ảnh hưởng đến dữ liệu trực tiếp?
  • Làm cách nào để tôi có thể mô phỏng / sơ khai các đối tượng trong bài kiểm tra Tích hợp khi chúng bị ẩn sau các lớp trừu tượng?
  • Tôi phải làm gì khi kiểm tra không thành công và dữ liệu trực tiếp bị để ở trạng thái không nhất quán?
  • Làm thế nào trong mã để tôi thực sự làm tất cả những điều này?

Có liên quan:


Đó là một số câu hỏi rộng, không phải một câu hỏi cụ thể.
Raedwald

Câu trả lời:


10

Đây là một câu trả lời bổ sung cho câu trả lời đã được đưa ra :

Nhìn qua mã của bạn, mã class GoogleAPIRequestnày có phần phụ thuộc được mã hóa cứng class Request. Điều này ngăn bạn kiểm tra nó một cách độc lập với lớp yêu cầu, vì vậy bạn không thể giả mạo yêu cầu.

Bạn cần làm cho yêu cầu trở nên khả thi, vì vậy bạn có thể thay đổi nó thành một mô hình trong khi thử nghiệm. Điều đó được thực hiện, không có yêu cầu HTTP API thực nào được gửi, dữ liệu trực tiếp không bị thay đổi và bạn có thể kiểm tra nhanh hơn nhiều.


2
Nhất trí 100%. Đó là lý do tại sao tôi đã sửa đổi thiết kế của mã để cho phép điều đó. Tuy nhiên, có nói rằng, có thể class GoogleAPIRequestcó một phương thức getNewRequest()có thể được chế tạo để trả về một Requestđối tượng bị chế nhạo (như một trong nhiều lựa chọn thay thế khả thi).
Jess Telford

1

Gần đây tôi đã phải cập nhật thư viện vì api mà nó kết nối đã được cập nhật.

Kiến thức của tôi không đủ để giải thích chi tiết, nhưng tôi đã học được rất nhiều điều từ việc xem mã. https://github.com/gridiron-guru/FosystemDataAPI

Bạn có thể gửi một yêu cầu như bình thường đến api và sau đó lưu phản hồi đó dưới dạng tệp json, sau đó bạn có thể sử dụng nó như một mô hình.

Hãy xem các bài kiểm tra trong thư viện này kết nối với một api bằng Guzzle.

Nó chế nhạo các phản hồi từ api, có rất nhiều thông tin trong tài liệu về cách hoạt động của thử nghiệm, nó có thể cung cấp cho bạn ý tưởng về cách thực hiện nó.

nhưng về cơ bản, bạn thực hiện một lệnh gọi thủ công tới api cùng với bất kỳ tham số nào bạn cần và lưu phản hồi dưới dạng tệp json.

Khi bạn viết thử nghiệm của mình cho lệnh gọi api, hãy gửi cùng các tham số và tải nó trong mô hình thay vì sử dụng api trực tiếp, sau đó bạn có thể kiểm tra dữ liệu trong mô hình mà bạn đã tạo có chứa các giá trị mong đợi.

Bạn có thể tìm thấy phiên bản Cập nhật của api được đề cập tại đây. Cập nhật Repo


0

Một trong những cách để kiểm tra các API bên ngoài như bạn đã đề cập, bằng cách tạo một mô hình giả và làm việc với mô hình đó với hành vi được mã hóa cứng như bạn đã hiểu.

Đôi khi mọi người gọi loại thử nghiệm này là thử nghiệm "dựa trên hợp đồng", nơi bạn có thể viết các bài kiểm tra chống lại API dựa trên hành vi mà bạn đã quan sát và mã hóa, và khi những bài kiểm tra đó bắt đầu không thành công, thì "hợp đồng bị phá vỡ". Nếu chúng là các thử nghiệm dựa trên REST đơn giản bằng cách sử dụng dữ liệu giả, bạn cũng có thể cung cấp chúng cho nhà cung cấp bên ngoài để chạy để họ có thể phát hiện ra vị trí / thời điểm họ có thể thay đổi API đủ để nó phải là phiên bản mới hoặc đưa ra cảnh báo về việc không bị ngược tương thích.

Tham khảo: https://www.thoughtworks.com/radar/techniques/consumer-driven-contract-testing


0

Xây dựng dựa trên những gì câu trả lời được bình chọn cao nói .... Đây là cách tôi đã thực hiện và hoạt động yên tĩnh.

  1. Đã tạo một đối tượng cuộn tròn giả
  2. Nói với người giả những thông số mà nó sẽ mong đợi
  3. Mô phỏng phản ứng của lệnh gọi curl trong hàm của bạn sẽ như thế nào
  4. Hãy để mã của bạn làm điều đó

    $curlMock = $this->getMockBuilder('\Curl\Curl')
                     ->setMethods(['get'])
                     ->getMock();
    
    $curlMock
        ->expects($this->once())
        ->method('get')
        ->with($URL .  '/users/' . urlencode($userId));
    
    $rawResponse = <<<EOL
    {
         "success": true,
         "result": {
         ....
         }
    }
    EOL;
    
    $curlMock->rawResponse = $rawResponse;
    $curlMock->error = null;
    
    $apiService->curl = $curlMock;
    
    // call the function that inherently consumes the API via curl
    $result = $apiService->getUser($userId);
    
    $this->assertTrue($result);
    
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.