Testing JSON APIs in Laravel

Introduction

Trong bài viết này chúng ta sẽ đề cập một số vấn đề liên quan đến việc testing JSON APIs trong một ứng dụng Laravel. Bản thân Laravel cũng cấp khá nhiều công cụ cho việc testing và chúng hoạt động khá hiệu quả. Tuy nhiên nội dung bài viết này sẽ chỉ đề cập đến một phần nhỏ trong số các công cụ đó. Nếu bạn chưa quen với việc testing khi làm việc với Laravel, Laravel Official Documentation sẽ luôn là người trợ giúp đắc lực. Documentation của Laravel thay đổi khá nhanh và liên tục, vì vậy không phải đọc một lần là hoàn thành, bản thân mình luôn tìm lại và đọc từng dòng một của documentation sau khoảng 2 đến 3 tháng.

The Basics

Khi testing JSON APIs, Laravel cung cấp khá nhiều phương thức để giả lập request và kiểm tra response. Cụ thể các request methods sẽ là: json, get, getJson, post, postJson, put, putJson, patch, patchJson, delete, deleteJson. Các phương thức trên đi theo cặp vì đơn giản không phải lúc nào response trả về cũng sẽ ở dạng JSON, nó có thể là Redirect Responses, View Responses hoặc File Responses . Các phương thức để kiểm tra response bao gồm: seeJsonEquals, seeJson, dontSeeJson, seeJsonStructure, seeJsonContains, seeJsonSubset. Trên thực tế còn khá nhiều các phương thức kiểm tra khác, tuy nhiên khi làm việc với JSON APIs những phương thức trên là cần và đủ (chúng ta có thể sử dụng thêm một số phương thức để kiểm tra response status code nếu cần thiết).

Đôi khi chúng ta cần biết response hiện tại là như thế nào và tại sao nó làm cho test case của chúng ta trả về màu đỏ, chúng ta có thể sử dụng $this->dump() để in nội dung của response gần nhất ra console, hoặc một cách dài hơn sẽ là dd($this->response->content() ($response là một public property).

Dưới đây là một ví dụ đơn giản:

    ...
    /** @test */
    public function it_validate_requested_profile_data()
    {
        $user = factory(User::class)->create();
        $this->setResourceOwner($user);
        $this->patch('/api/v1/profile', ['username' => 'foo bar', 'full_name' => '']);
        $this->seeStatusCode(422);
        $this->seeJsonStructure([
            'success',
            'title',
            'data' => [
                'username',
                'full_name',
            ],
        ]);
    }
    ...

Trong ví dụ này, chúng ta đang test một chức năng liên quan đến việc cho phép người dùng cập nhật thông tin profile của họ. Đầu tiên chúng ta cần một người dùng mẫu được tạo bằng cách sử dụng Model Factory. Đối với dòng tiếp theo $this->setResourceOwner($user);, nó dùng để mình họa cho trường hợp ứng dụng của chúng ta đang dùng OAuth2 bên trong phương thức này chúng ta sẽ thực hiện mocking một số thứ liên quan và trả về ID của người dùng. Điều chúng ta quan tâm đến sẽ là những dòng tiếp theo. Chúng ta sẽ giả lập một PATCH request đến một API endpoint có tên là /api/v1/profile, request này sẽ cần dữ liệu kèm theo vì chúng ta đang thực hiện việc update thông tin người dùng (một associative array trong trường hợp này). Sau khi thực hiện request trên, chúng ta sẽ tiến hành verify response được trả lại, bắt đầu bằng việc kiểm tra status code. Status code được trả về sẽ là 422 - Unprocessable Entity vì full_name là một trường required. Response được trả về sẽ là các thông báo lỗi và chúng ta sử dụng seeJsonStructure để kiểm tra cấu trúc của response. Chúng ta sẽ không muốn hard-code nội dung của error message ở đây vì đơn giản nó có thể sẽ thay đổi sau này và chúng ta sẽ cần quay lại và chỉnh sửa test case.

How to use?

Như chúng ta đã thấy có khá nhiều phương thức để kiểm tra nội dụng của response, tuy nhiên chúng sẽ được sử dụng như thế nào. Trong phần này của bài viết, chúng ta sẽ tìm hiểu kĩ càng hơn về những phương thức đó:

  • seeJsonEquals: phương thức này khá dễ hiểu và được sử dụng để kiểm tra xem nội dụng của JSON response tra về trùng khớp với array mà chúng ta cung cấp. Bên trong phương thức đó response sẽ được json_decodesort, và cuối cùng sẽ gọi đến PHPUnit assertEquals để kiểm tra.

  • seeJson: dựa vào tên của phương thức chúng ta có thể đoán được nó sẽ là một thứ gì đó chung và rộng hơn, thực tế phương thức này sẽ sử dụng seeJsonEquals trước và nếu kết quả không khớp nó sẽ fallback về sử dụng seeJsonContains.

  • dontSeeJson: là dạng inverse của seeJson.

  • seeJsonStructure: như trong ví dụ phía trên, đôi khi chúng ta không cần xác minh rõ ràng nội dung (value) tương ứng với một trường (key) bên trong response. Phương thức này được sử dụng để kiểm tra sự tồn tại của các key bên trong response mà không cần quan tâm đến nội dụng của chúng. Phương thức này khá hữu ích khi nội dung response trả về là dynamic và không gói gọn trong một khoảng nào đó.

  • seeJsonContains: phương thức này hay gây nhầm lẫn cho nhiều người, nếu nhìn thoáng qua chúng ta có thể nghĩ chúng ta đang kiểm tra xem nội dụng response trả về có bao hàm một phần nhỏ nào đó hay không. Điều đó là đúng nhưng chưa đủ, thực chất phương thức này sẽ loop qua từng cặp key-value bên trong data mà chúng ta muốn xác thực, format cặp key-value đó thành dạng JSON string và kiểm tra xem string đó có tồn tại bên trong json_encode version của response trả về hay không. Hiểu một cách đơn giản, chỉ cần cặp key-value mà chúng ta muốn xác thực xuất hiện bên trong nội dung respose không quan tâm đến thứ tự hay vị trí của cặp key-value đó bên trong response, seeJsonContains sẽ trả về cho chúng ta màu xanh. Xét một ví dụ nhỏ như sau:

Nội dụng của response trả về như sau

     'user' => [
         'id' => '1',
         'attributes' => [
             'name' => 'Foo Bar',
             'username' => 'foo_bar',
             'email' => '[email protected]',
         ],
     ]

Nếu chúng ta sử dụng seeJsonContains như sau, kết quả trả về vẫn sẽ là màu xanh

     $this->seeJsonContains([
         'id' => '1',
         'name' => 'Foo Bar',
         'username' => 'foo_bar',
         'email' => '[email protected]',
     ]);
  • seeJsonSubset: phương thức này và phương thức seeJsonContains không có mặt trong documentation của Laravel mặc dù chúng khá hữu dụng. Phương thức này đơn giản gọi đến PHPUnit assertArraySubset.

Conclusion

Chúng ta có thể tự đặt câu hỏi rằng nên sử dụng phương thức nào khi testing JSON APIs. Mỗi phương thức đều có những điểm mạnh và hạn chế riêng. Đối với mình seeJsonStructureassertArraySubset là hai phương thức mình sử dụng nhiều nhất đơn giản vì chúng khá flexible và dễ hiểu và nó phù hợp với khá nhiều trường hợp khi testing.

Đối với những phương thức còn lại, chúng đều có use-case riêng, tuy nhiên nếu không nắm rõ chức năng và cách dùng của mỗi phương thức. Đôi khi chúng sẽ gây ra những nhầm lẫn khó hiều. Đôi lúc khi testing, chúng ta chỉ cố để tất cả các test trả về màu xanh mà không quan tâm đến cách để thực hiện điều đó.

Trong bài viết này, mình đã trình bày một góc rất nhỏ khi testing ứng dụng Laravel. Hi vọng bài viết sẽ giúp ích một phần nào đó cho các bạn.


All Rights Reserved