Hướng dẫn phpunit composer - nhà soạn nhạc phpunit

Hướng dẫn phpunit composer - nhà soạn nhạc phpunit

Giới thiệu

Biết rằng Unit Test quan trọng và mang lại nhiều lợi ích là thế nhưng việc áp dụng, thực hiện như thế nào cho đúng và hiệu quả là một điều không dễ.


Bài viết này là bài bài đầu tiên trong loạt bài hướng dẫn thực hiện Unit Test trong PHP, được dịch từ blog của tác giả Juan Treminio. Đây có lẽ là tutorial chi tiết nhất mà tôi đọc được. Bài dịch có một số update về version của PHP và PHPUnit so với blog của tác giả. Dưới đây là link đến các phần khác của tutorial (sẽ update link khi viết xong):

  • PHP Unit Test 101: Giới thiệu về PHPUnit
  • PHP Unit Test 201: Làm quen với Test case, Assertions và data provider
  • PHP Unit Test 301: Test các phương thức Protected/Private
  • PHP Unit Test 401: Tạo báo cáo Coverage Reports và chỉ số CRAP
  • PHP Unit Test 501: Sử dụng Mock Objects, Stub Methods và Dependency Injection
  • PHP Unit Test 601: Mock Methods và Constructor Overriding

Loạt bài này sẽ giới thiệu cho bạn các khái niệm cơ bản về testing. Nó sẽ cho bạn biết vì sao việc sử dụng

sudo apt install php-xml php-json php-mbstring
2 là không tốt, tại sao
sudo apt install php-xml php-json php-mbstring
3 tuyệt vời, sự khác biệt giữa
sudo apt install php-xml php-json php-mbstring
4 và
sudo apt install php-xml php-json php-mbstring
5 và làm thế nào để yêu cái thanh màu đỏ, xanh ấy.

Tôi cũng sẽ đề cập một chút đến Test-Driven Development (TDD) nhưng sẽ không tập trung nhiều vào nó vì tôi nghĩ rằng để code của bạn trở nên testable và làm sao để test nó một cách chính xác đã là một thử thách đủ lớn cần phải học trước tiên, mà không cần phải quá quan tâm đến một quy trình phát triển hoàn toàn khác.

Bài viết sẽ không nói nhiều về việc tại sao bạn nên viết test, tại sao testing là cần thiết và những lợi ích của testing. Nếu bạn muốn được thông não về những điều đó, tôi khuyên bạn nên đọc qua cuốn Real-World Solutions for Developing High-Quality PHP Frameworks and Applications của Sebastian Bergmann và ghé thăm blog của bạn thân tôi Chris Hartjes, The Grumpy Programmer's blog, người mà đã la hét vào cộng đồng PHP trong nhiều năm rằng họ nên viết test. Ngoài ra, trên Viblo cũng đã có rất nhiều bài viết bàn về Unit Test, các bạn rất nên đọc qua:

  • Tản mạn về Testing
  • Vai trò của Unit Test
  • Writing Great Unit Tests

Trước khi bắt đầu

Bài viết giả sử bạn đã cài đặt PHP. Tôi rất khuyên bạn nên sử dụng một máy ảo dành riêng cho môi trường phát triển thay vì thiết lập nó ngay trên hệ điều hành của bạn. Bạn có thể đọc qua bài viết Setting Up a Debian VM, Step by Step, hướng dẫn từng bước sử dụng Virtual Box để thiết lập PHP và web server trên Debian, hoặc tham khảo các bài viết trên Viblo về sử dụng Docker để tạo môi trường phát triển.

Chúng ta cũng sẽ sử dụng dòng lệnh để chạy PHPUnit... Nếu bạn đang quen với việc làm mọi thứ trên giao diện GUI, thì đây cũng là thời gian cho bạn làm quen với Terminal.

Cài đặt PHPUNIT

PHPUnit 6.2 yêu cầu PHP 7 và các thư viện

sudo apt install php-xml php-json php-mbstring
6,
sudo apt install php-xml php-json php-mbstring
7 và mbstring, thường đã được cài đặt mặc định khi cài đặt PHP. Kiểm tra bằng cách:

php -r 'print_r(get_loaded_extensions());'

Nếu chưa có thì cài thêm vào, vd đối với Ubuntu ^16.04:

sudo apt install php-xml php-json php-mbstring

Tính năng tạo báo cáo Code Coverage yêu cầu Xdebug (hướng dẫn cài đặt, bài viết giới thiệu của tác giả) (phiên bản 2.5.0 hoặc mới hơn) và thư viện

sudo apt install php-xml php-json php-mbstring
8 (đã có sẵn khi cài PHP).

Cách đơn giản nhất là cài đặt thông qua Composer. Nếu bạn không biết Composer là gì hay cách sử dụng nó thì bạn nên đọc bài viết, Composer Namespaces in 5 minutes. Nó giới thiệu bạn với Composer nói chung và cách sử dụng PSR-0 cho việc autoloading.

Cài đặt:

composer require --dev phpunit/phpunit ^6.2

Chạy PHPUnit

Sau khi cài đặt xong hãy chú ý đến file

sudo apt install php-xml php-json php-mbstring
9. Đây là file dùng để thực thi PHPUnit. Chạy lệnh:
sudo apt install php-xml php-json php-mbstring
9, nó sẽ hiển thị tất cả các tùy chọn help.
Hướng dẫn phpunit composer - nhà soạn nhạc phpunit

Cấu trúc project

Do chúng ta đang sử dụng Composer nên sẽ cần cấu trúc project một chút để nó hoạt động với autoloader. Các file mã nguồn sẽ được viết vào thư mục

composer require --dev phpunit/phpunit ^6.2
1 với namespace là
composer require --dev phpunit/phpunit ^6.2
2 và unit tests sẽ được viết vào thư mục
composer require --dev phpunit/phpunit ^6.2
3 với namespace là
composer require --dev phpunit/phpunit ^6.2
4.

Update file

composer require --dev phpunit/phpunit ^6.2
5 của bạn như sau:

{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}

Sau đó chạy lệnh để update autoloader:

composer dump-autoload

Như vậy cấu trúc project của chúng ta như sau:

phpunit-tut/
|-- composer.json
|-- composer.lock
|-- phpunit.xml
|-- src
|-- tests
|-- vendor

Cấu hình file phpunit.xml

Bạn có thể sử dụng các tùy chọn trong dòng lệnh để cấu hình cho phpunit tuy nhiên có 1 cách đơn giản hơn đó là sử dụng file cấu hình

composer require --dev phpunit/phpunit ^6.2
6.

Trong folder root

composer require --dev phpunit/phpunit ^6.2
7 tạo file
composer require --dev phpunit/phpunit ^6.2
6:


<phpunit colors="true">
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory>./tests/directory>
        testsuite>
    testsuites>
phpunit>

Đây là file cấu hình đơn giản nhất, tuy nhiên có 2 điểm quan trọng:

  • composer require --dev phpunit/phpunit ^6.2
    
    9 làm cho kết quả test được bôi màu
  • {
        "require-dev": {
            "phpunit/phpunit": "^6.2"
        },
        "autoload": {
            "psr-4": {
                "App\\": "src/"
            }
        },
        "autoload-dev": {
            "psr-4": {
                "Tests\\": "tests/"
            }
        }
    }
    
    0 cho PHPUnit biết nơi lưu các file tests

CONVENTIONS

Trong loạt bài viết này sẽ có một số conventions giúp cho bạn sử dụng PHPUnit dễ dàng hơn, mặc dù có một số convention là không bắt buộc.

Cấu trúc và tên file

Convention đầu tiên là về cấu trúc file và tên file. Các file tests nên ánh xạ với các file mã nguồn tương ứng trong từng thư mục và tên file nên được đặt giống với file mã nguồn cộng thêm từ

{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
1 ở sau. Ví dụ, nếu chúng ta có các file mã nguồn như sau:

./src/Foo.php
./src/Bar.php
./src/Controller/Baz.php

Thì các file tests nên được tổ chức như sau:

./tests/FooTest.php
./tests/BarTest.php
./tests/Controller/BazTest.php

Tên lớp

Tên lớp phải khớp hoàn toàn với tên file, điều này cũng nên áp dụng với các file mã nguồn khác.

Tên các method (test)

Các phương thức trong các lớp test nên được bắt đầu với từ

{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
2 (mặc dù bạn có thể sử dụng annotation
{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
3 nhưng nên thống nhất viết theo 1 loại cho đồng bộ). Tên phương thức nên có tính mô tả cái gì sẽ được test trong phương thức đó và cũng nên bao gồm tên phương thức đang được test. Tên method không cần phải ngắn hoặc viết tắt.

Ví dụ, bạn đang test phương thức có tên là

{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
4 và bạn muốn test trường hợp mật khẩu khớp với account, bạn nên đặt tên method trong file test là
{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
5.

Sự dài dòng là một lợi ích khi test, bởi vì khi bạn có 1 trường hợp test failed và bạn sẽ có rất nhiều trường hợp test failed, bạn sẽ thấy được tên method là gì và biết chính xác trường hợp nào đang bị failed.

Public methods

PHPUnit không thể chạy các method test ở dạng

{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
6 hay
{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
7, chúng phải là
{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
8. Do đó, đối với các method test hay các method helpers (chẳng hạn data provider method phải ở dạng
{
    "require-dev": {
        "phpunit/phpunit": "^6.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
8. Mục đích của chúng ta là viết test thôi nên không cần phải lo lắng về tính đóng gói.

Extends PHPUnit

Các class test phải là lớp con cháu của lớp

composer dump-autoload
0.

Unit Test đầu tiên

Unit Test đầu tiên của chúng ta sẽ ngắn và stupid thôi nhưng nó sẽ cho bạn biết yêu cầu nhỏ nhất của 1 test: Tạo 1 file

composer dump-autoload
1:
Hướng dẫn phpunit composer - nhà soạn nhạc phpunit
nhưng nó sẽ cho bạn biết yêu cầu nhỏ nhất của 1 test: Tạo 1 file
composer dump-autoload
1:



namespace Tests;

use PHPUnit\Framework\TestCase;

class StupidTest extends TestCase
{
    //
}

Không có gì đặc biệt cả, chỉ là cho bạn thấy các conventions được áp dụng như thế nào thôi.

Để thử cho biết, bạn muốn chắc chắn một thứ gì đó nó có bằng true hay không (chẳng hạn 1 hàm nào đó có return true hay không). Assertions là một trong những phương thức cơ bản được dùng trong unit test, và tôi sẽ đề cập nó xuyên suốt trong loạt bài này.

Còn bây giờ, bạn hãy tạo 1 method tên là

composer dump-autoload
2. Ngu lắm phải không ?
Hướng dẫn phpunit composer - nhà soạn nhạc phpunit
?

sudo apt install php-xml php-json php-mbstring
0

Tiếp theo là đến đoạn test code. Bây giờ chỉ đơn giản thôi như thế này thôi, chưa cần nghĩ sâu sắc quá:

sudo apt install php-xml php-json php-mbstring
1

Dat Green bar

Từ thư mục root của project, chạy PHPUnit:

composer dump-autoload
3

Vâng, bạn sẽ thấy được cái thanh màu xanh ấy, nó biểu thị tất cả các test case đã được pass. Tuy nhiên cũng đừng chăm chăm làm sao cho ra được cái màu xanh ấy, không phải viết code chỉ để pass test, mà mục đích ở đây là

composer dump-autoload
4
Hướng dẫn phpunit composer - nhà soạn nhạc phpunit
Hướng dẫn phpunit composer - nhà soạn nhạc phpunit

Bạn đã chạy 1 file test, 1 test case trong đó có 1 assertion.

Kết luận

Qua bài này, bạn đã cài được PHPUnit, thiết lập một số cấu hình và chạy unit (super stupid) test đầu tiên.

Xin chúc mừng, bạn đã đi qua bước đầu tiên để đến gần hơn với thiên đường tester!

Trong bài tiếp theo, tôi sẽ giải thích về assertions, giới thiệu một số annotation của PHPUnit trong đó có

composer dump-autoload
5 và giúp bạn viết những unit test thực thụ đầu tiên.
Hướng dẫn phpunit composer - nhà soạn nhạc phpunit