PHP Unit Test 201: Làm quen với Test case, Assertions và data provider
https://viblo.asia/p/php-unit-test-201-lam-quen-voi-test-case-assertions-va-data-provider-ByEZkW7yZQ0
C:\xampp82\htdocs\test-project\phpunit.xml
C:\xampp82\htdocs\test-project\composer.json
C:\xampp82\htdocs\test-project\src\URL.php
C:\xampp82\htdocs\test-project\tests\URLTest.php
PHP Unit Test 201: Làm quen với Test case, Assertions và data provider
Bài đăng này đã không được cập nhật trong 2 năm
Trong bài đầu tiên của series này, chúng ta đã đi qua cách cài đặt và cấu hình PHPUnit cho 1 project PHP, một số conventions khi thực hiện Unit test trong PHP và trải nghiệm với unit test đầu tiên. Trong bài này, chúng ta sẽ tìm hiểu một số khái niệm quan trọng trong unit test và đi vào thực hành nhiều hơn.
ASSERTIONS
Assertion là gì? Theo định nghĩa từ Wikipedia:
In computer programming, an assertion is a statement that a predicate (Boolean-valued function, a true–false expression) is expected to always be true at that point in the code
Assertion chỉ đơn giản là 1 câu lệnh nhằm mục đích xác nhận một khẳng định là luôn đúng tại đoạn code đó. Hiểu theo cách khác, Assertion định nghĩa điều bạn muốn nó xảy ra (VD: Tôi muốn hàm này trả về false => tôi assert return value là false, tôi muốn hàm kia trả về mảng có chứa 5 phần tử => tôi assert array size = 5, tôi muốn kết quả thu được lơn hơn 100,...)
Assertion trả về true thì sẽ pass unit test, ngược lại sẽ fail.
Trong ví dụ đầu tiên:
Chúng ta đã assert rằng true là true ( if (true == true) ). Không có gì đặc biệt ở đây, điều bạn thấy là điều bạn nhận được với assertions.
Nếu chúng ta assert rằng false là true, chúng ta sẽ nhận được 1 test fail:
Nhưng nếu chúng ta muốn assert rằng false là false ( if (false == false) ) thì sao?
Unit test này được pass vì câu lệnh assertion của chúng ta trả về true, mặc dù cho phương thức được gọi là assertFalse()
.
PHPUnit cung cấp rất nhiều assertions được liệt kê tại đây. Bạn không phải sử dụng tất cả. Phần lớn bạn sẽ sử dụng các assertions assertArrayHasKey()
, assertEquals()
, assertFalse()
, assertSame()
và assertTrue()
, chúng ta sẽ tập trung vào các assertion này trước. Các hàm PHPUnit Assertions cũng giống như 1 hàm bình thường và giá trị trả về là true hoặc false, bạn hoàn toàn có thể tự viết các assertion, tôi sẽ đề cập phần này sau.
Unit test (có ích) đầu tiên
Unit test có ích đầu tiên của chúng ta sẽ là unit test cho một hàm chuyển đổi từ string thông thường sang dạng url slug, ví dụ hàm này sẽ biến đổi chuỗi: "This string will be sluggified" sang "this-string-will-be-sluggified".
Tạo file src/URL.php
:
Tôi thấy đoạn code này là ví dụ rất tốt để thực hành unit test đầu tiên vì nó dễ hiểu và có rất nhiều trường hợp có thể gây ra lỗi.
Chúng ta bắt đầu viết test với file tests/URLTest.php
:
Chúng ta chạy phpunit ngay sau khi tạo file test và bộ khung của nó để chắc chắn rằng chúng ta không bị lẫn lộn trong tên file hay tên test class. Điều này sẽ giúp tránh các trường hợp không mong đợi trong tương lai khi bộ test tất cả đều pass nhưng bạn nhận ra PHPUnit đã không thực sự chạy file test của bạn có thể do sai sót cách đặt tên.
Bước tiếp theo, chúng ta sẽ test trường hợp đầu tiên: chúng ta muốn xác nhận rằng App\URL::sluggify()
sẽ trả về 1 string đã được biến đổi sang dạn slug, method test sẽ được viết như sau:
Tiếp theo, chúng ta sẽ bắt đầu viết phần thân cho test method. Mong muốn của chúng ta trong test case này là:
"This string will be sluggified" sẽ được chuyển thành "this-string-will-be-sluggified".
Để test method App\URL::sluggify()
, chúng ta cần khởi tạo 1 đối tượng của class URL:
Bây giờ, chúng ta sẽ lấy ra kết quả từ phương thức ::sluggify()
:
Bước cuối cùng đó là assert rằng $result
đúng như mong muốn của chúng ta đã được khai báo trong biến $expectedResult
. Assertion phù hợp nhất ở đây là assertEquals()
:
Các kịch bản test khác
Unit test khởi đầu của chúng ta đã pass, thật tuyệt. Tuy nhiên, có 1 vấn đề là chúng ta mới chỉ test 1 string chỉ chứa các ký tự A-Z và dấu cách. Điều gì sẽ xảy ra nếu string có chứa số hay các ký tự đặc biệt ( (~!@#$%^&*()_+))? Và với các ký tự không phải English thì sao? Trường hợp string rỗng thì như thế nào? Có quá nhiều trường hợp cần phải test. Một bộ test suite chuẩn khi nó bao phủ được tất cả các khả năng có thể xảy ra, vì vậy chúng ta sẽ viết test cho một số kịch bản khác:
Ghi chú: do hàm App\URL::sluggify()
sử dụng hàm iconv
của PHP nên nó phụ thuộc vào locale CTYPE của hệ thống (issue), nếu unit test bị fail bạn nên kiểm tra locale của hệ thống có hỗ trợ UTF-8 hay không. VD đối với Linux (Debian/Ubuntu): Kiểm tra locale CTYPE hiện tại:
Danh sách các locale hệ thống hỗ trợ
Vấn đề trùng lặp code
Bạn có thể dễ dàng nhận thấy code test của chúng ta đang có vấn để lớn là trùng lặp code.
May mắn là PHPUnit đã có hỗ trợ công cụ để chúng ta khắc phục điều này.
Annotations trong PHPUnit
Annotations là các flag đặc biệt được khai báo trong docblocks của method:
PHPUnit cung cấp rất nhiều annotations hữu ích, nhưng trước hết chúng ta đề hãy cập đến @dataProvider
.
PHPUnit định nghĩa data providers như sau:
A test method can accept arbitrary arguments. These arguments are to be provided by a data provider method.
Tạm dịch là: 1 test method có thể chấp nhận nhiều input khác nhau. Các tham số này được cung cấp bởi method data provider.
Hiểu đơn giản, 1 method data provider có thể được sử dụng để tạo ra nhiều tập input để truyền vào 1 test method, khắc phục vấn đề phải tạo nhiều method test như chúng ta đã thực hiện ở trên.
Thay vì tạo ra nhiều phương thức test, bạn chỉ cần tạo ra một phương thức duy nhất với các tham số tương ứng với dữ liệu biến đổi giữa các phép thử, và tạo một phương thức cung cấp dữ liệu để test. VD:
Một method data provider trả về 1 mảng các tập input. Trong ví dụ trên, chúng ta có 5 tập input đầu vào để test. Mỗi tập là 1 mảng các các giá trị được truyền vào test method theo thứ tự trong mảng. Ví dụ với tập input đầu:
Nó sẽ được truyền vào method testFoo($variableOne, $variableTwo)
với $variableOne
là test 1, variable one
và $variableTwo
là test 1, variable two
.
Bây giờ, chúng ta sẽ áp dụng vào unit test lần trước. Sửa lại file tests/URLTest.php
:
Chạy lại PHPUnit:
Huzzah!
Kết luận
Trong bài này, chúng ta đã tìm hiểu về assertions, thực hành các unit test thực sự và tìm hiểu về @dataProvider
annotation.
Vẫn còn khá nhiều thứ cần phải học, nhưng trước hết bạn nên thực hành test các code đơn giản (không có phụ thuộc bên ngoài method).
Trong bài tiếp theo, chúng ta sẽ học cách test các method có các dependency bên ngoài, tìm hiểu các khái niệm mocks, stubs và sự khác nhau của chúng, tìm hiểu tại sao nên hạn chế sử dụng static method và sự hữu ích của dependency injection.
Hẹn gặp lại các bạn trong bài tiếp theo.
Last updated
Was this helpful?