Mình là một developer, mình muốn tạo ra nhũng thứ tốt đẹp cho thế giới này bằng những dòng code (nói quá lên chút :D). Và mình cũng như các bạn, không muốn sản phẩm của mình tạo ra đầy những bug, phải không? Nhưng tạo sao chúng ta lại ngại việc test đến như vậy? Một phần nguyên nhân là việc test tốn nhiều time, setup phức tạp và luôn phải lặp đi lặp lại công việc nhàn chán này. Mình đã đi tìm cách thực hiện việc này một cách tự động. Và thật tuyệt vời, mình đã tìm thấy PHPUnit.
PHPUnit is a unit testing framework for the PHP programming language. It is an instance of the xUnit architecture for unit testing frameworks. PHPUnit was created by Sebastian Bergmann and its development is hosted on GitHub. — Wikipedia
Mình hiểu nôm na thì nó là một thư viện, một công cụ giúp ta tạo ra các script unit test. Còn unit là đơn vị, mà đơn vị to hay nhỏ thì tuỳ vào trường hợp, như là met, minimet và kilomet vậy.
Có nhiều cách để bắt đầu với PHPUnit. Ở đây mình giới thiệu 1 cách sử dụng composer
để cài đặt PHPUnit.
1
2
3
$ cd /path/to/project
$ composer require phpunit/phpunit ~4.8
$ ./vendor/bin/phpunit -v
Bạn tạo file phpunit.xml
để thiết lập các tham số cho PHPUnit khi chạy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<phpunit colors="false" verbose="true" stopOnError="false"
stopOnFailure="false" bootstrap="tests/bootstrap.php">
<!-- Change php settings -->
<php>
<ini name="memory_limit" value="-1"/>
<ini name="apc.enable_cli" value="1"/>
</php>
<!-- Add any test suites you want to run here -->
<testsuites>
<testsuite name="Test Source">
<directory>tests</directory>
</testsuite>
</testsuites>
<!-- Ignore vendor tests in code coverage reports -->
<filter>
<blacklist>
<directory>vendor</directory>
<directory>bin</directory>
</blacklist>
<whitelist>
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>
Trong file phpunit.xml
trên, mình có khai báo là sẽ dùng file tests/bootstrap.php
để khởi tạo các yếu tố cần thiết cho việc giả lập môi trường test và load các class test.
1
2
3
4
5
6
7
<?php
// tests/bootstrap.php
// Enable Composer autoloader
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php';
// Register test classes
$autoloader->addPsr4('YourOrg\\YourProject\\Tests\\', __DIR__);
Tiếp theo, chúng ta bắt đầu viết các class test và đừng quên đúng conventions của nó.
Test
và thường tên giống với tên class cần testPHPUnit_Framework_TestCase
test
và các method này phải là public
.
Ví dụ method testVerifyAccount()
để test cho method verifyAccount()
Lập bảng matrix test case
Bảng matrix cho test method matchingAccount($username, $password): boolean
Viết test script theo bảng matrix, có thể dùng tính năng dataProvider để thực hiện test với nhiều case
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
//...
/**
* Test `matchingAccount()` method
*
* @param string $username
* @param string $password
* @param boolean $expected
* @dataProvider dataTestMatchingAccount
*/
public function testMatchingAccount($username, $password, $expected)
{
$result = $this->authenticator->matchingAccount($username, $password);
$this->assertSame($expected, $result);
}
/**
* Data for test `matchingAccount()` method
*
* @return array
*/
public function dataTestMatchingAccount()
{
return [
['', '', false],
['', 'abc12345', false],
['oanhnn', '', false],
['oanhnn', 'abc12345', true],
['oanhnn', '12345678', false],
['oanh', '', false],
['oanh', 'abc12345', false],
['oanh', '12345678', false],
];
}
Bây giờ chạy test thôi:
1
$ ./vendor/bin/phpunit
Có nhiều cách để thêm trigger cho phép chúng ta tự động chạy script test,
ở đây mình xin giới thiệu 1 cách là sử dụng git hook pre-commit
. Sau khi
đã có đoạn script test, bạn tìm đến thư mục /path/to/project/.git/hooks/
và tạo file pre-commit
với nội dung như sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/php
<?php
// Hook configuration
$project = 'Training PHPUnit 01';
// Tell the commiter what the hook is doing
echo PHP_EOL;
echo "Starting unit tests...\n";
// Execute project unit tests
exec('phpunit', $output, $returnCode);
// if the build failed, output a summary and fail
if ($returnCode !== 0) {
// find the line with the summary; this might not be the last
while (($minimalTestSummary = array_pop($output)) !== null) {
if (strpos($minimalTestSummary, 'Tests:') !== false) {
break;
}
}
// output the status and abort the commit
echo "Test suite for '{$project}' failed:\n";
echo $minimalTestSummary;
echo PHP_EOL;
exit(1);
}
echo "All tests for '{$project}' passed.\n";
echo PHP_EOL;
exit(0);
Sau đó thêm quyền thực thi cho file pre-commit
và commit thử bạn nhé!
1
2
3
4
5
6
7
8
9
10
$ cd /path/to/project
$ vi .git/hooks/pre-commit
$ chmod a+x .git/hooks/pre-commit
$ git commit -am "Added changes"
Starting unit tests...
All tests for 'Training PHPUnit 01' passed.
[master xxxxxxx] Added changes
1 files changed, 1 insertions(+), 1 deletions(-)
Thật tuyệt vời phải không, sao chúng ta không làm việc này thành 1 quy trình sản xuất phần mềm? Thật ra từ lâu người ta đã nghĩ tới việc này và tạo ra mô hình TDD.
Định nghĩa của Wikipedia:
Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards.
Có thể hiểu nôm na nó là một quá trình phát triển phần mềm mà trong đó người ta sẽ viết
script auto test để mô tả yêu cầu của tính năng mới rồi mới đi tạo lượng code tối thiểu để pass qua phần test đó.
Cuối cùng họ sẽ chỉnh sửa lại phần code để phù hợp với các yêu cầu, các chuẩn hoá khác.
Các bước trong quá trình TDD:
TDD Global Lifecycle
Như vậy chúng ta đã biết cách tạo script test đơn giản với PHPUnit, biết cách thêm trigger tự động test trước mỗi lần commit với git, và biết TDD là gì, áp dụng nó và công việc gồm những bước nào. Ở bài sau, cũng ta sẽ cùng trao đổi sâu hơn về cách viết unit test với PHPUnit.
Comments