Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. PHP
Code

Hướng Dẫn Test-Driven Development Cho Người Mới

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called Test-Driven PHP.
It's Time To Dig In
Test-Driven Development in PHP: First Steps

Vietnamese (Tiếng Việt) translation by Brian Vu (you can also view the original English article)

Việc kiểm tra mã lệnh thật là phiền phức, nhưng hệ quả của việc ngó lơ nó có thể còn khó chịu hơn! Trong bài viết này, chúng ta sẽ sử dụng test-driven development để viết và kiểm thử mã lệnh của chúng ta hiệu quả hơn.


Test-Driven Development là gì?

Kể từ buổi bình minh của kỷ nguyên máy tính, các lập trình viên và lỗi (bug) đã luôn đối đầu với nhau. Đó là một việc không thể tránh khỏi. Thậm chí cả các lập trình viên vĩ đại nhất cũng là nạn nhân của định mệnh kỳ quặc này. Không có mã lệnh nào là an toàn cả. Đó là lý do tại sao chúng ta phải kiểm thử. Các lập trình viên, ít nhất là những người chuẩn mực, kiểm tra mã lệnh của họ bằng cách chạy nó trên môi trường phát triển (dev env) để đảm bảo nó chạy đúng như mong đợi.


Lập trình viên chuẩn mực, luôn thực hiện kiểm thử chương trình.
Hình ảnh hỗ trợ bởi http://www.youthedesigner.com

Lập trình viên thiếu chuẩn mực, không bao giờ kiểm thử chương trình.
Hình ảnh hỗ trợ bởi http://www.internetannoyanceday.com

Test-driven deveplopment là một kỹ thuật lập trình đòi hỏi bạn phải thực hiện song song việc viết mã chương trình cùng với viết mã kiểm thử tự động cho mã lệnh đó. Điều này đảm bảo rằng mã của bạn được kiểm thử - và cho phép bạn kiểm thử lại mã lệnh của mình một cách nhanh chóng và dễ dàng, vì quy trình đã được tự động hóa.

Nó làm việc như thế nào?

Test-driven development, từ giờ chúng ta sẽ gọi tắt là TDD, xoay quanh một chu kỳ phát triển vòng lặp ngắn như sau:

  1. Trước khi viết bất kỳ mã chương trình nào, bạn phải viết mã lệnh kiểm thử tự động cho đoạn mã đó trước. Trong khi viết kiểm thử tự động, bạn phải tính đến toàn bộ các yếu tố đầu vào, lỗi, cũng như đầu ra, có khả năng xảy ra. Cách này giúp đầu óc của bạn không phải bận tâm bởi bất kỳ mã lệnh nào đã được viết trước đó.
  2. Đối với mỗi lần đầu tiên bạn chạy kiểm thử tự động, việc kiểm thử nên trả về kết quả fail - viêc này sẽ thể hiện sự chưa sẵn sàng của mã lệnh chương trình.
  3. Sau đó, bạn có thể bắt đầu viết mã chương trình. Bởi vì chúng ta đã xây dựng kiểm thử tự động, miễn là việc kiểm thử đang bị thực thi fail, để nói lên rằng mã chương trình vẫn chưa sẵn sàng. Mã chương trình chỉ có thể coi là đã được sửa cho tới khi nó pass tất cả các assertion.
  4. Khi mã chương trình đã pass kiểm thử, bạn có thể bắt đầu làm sạch mã, bằng các kỹ thuật refactoring. Miễn là mã lệnh vẫn pass kiểm thử, thì nó vẫn hoạt động tốt. Bạn không còn phải lo lắng về việc những thay đổi có thể làm phát sinh lỗi mới.
  5. Lặp lại quy trình này với các phương thức hoặc chương trình khác.

Thật tuyệt phải không nào, nhưng nó sẽ tốt hơn việc kiểm thử thông thường ra sao?

Bạn đã bao giờ cố tình bỏ qua kiểm thử một chương trình bởi vì:

  • Bạn cảm thấy lãng phí thời gian cho kiểm thử, chỉ bởi một sự thay đổi nhỏ trong mã lệnh.
  • Bạn cảm thấy lười kiểm thử lại mọi thứ?
  • Bạn không đủ thời gian để làm kiểm thử bởi vì người quản lý dự án muốn có được thành phẩm càng sớm càng tốt?
  • Bạn tự nhủ với bản thân là "sẽ làm nó vào ngày mai"?
  • Bạn phải chọn lựa giữa việc kiểm thử bằng tay, hay xem phần cuối của chương trình truyền hình bạn yêu thích (Big Bang Theory)?

Trong hầu hết mọi trường hợp, không có gì xảy ra, và bạn sẽ chuyển mã lệnh của bạn thành sản phẩm một cách thành công mà không có bất kỳ biến cố nào. Nhưng đôi lúc, sau khi bạn đưa ra thành phẩm, mọi thứ đều sai lệch. Bạn bị mắc kẹt với hàng trăm lỗ hổng cần phải sửa chữa trong một sản phẩm tồi tệ, mỗi phút chúng càng xuất hiện nhiều hơn. Bạn sẽ không muốn bị rơi vào hoàn cảnh này đâu.


Kệ nó, cứ chuyển mã lệnh thành sản phẩm!
Hình ảnh hỗ trợ bởi http://phenomenaonbreak.wordpress.com

TDD ra đời nhằm loại bỏ sự biện hộ của chúng ta. Khi 1 sản phẩm được phát triển theo TDD, nó cho phép chúng ta thực hiện việc thay đổi, kiểm thử một cách nhanh chóng và hiệu quả. Mọi thứ chúng ta cần làm là chạy kiểm thử tự động, và chỉ có vậy! Nếu chương trình pass toàn bộ quá trình kiểm thử tự động, nghĩa là nó thực sự tốt - nếu không, đơn giản là chúng ta đã bẻ gãy đâu đó bằng các thay đổi. Bằng cách biết được chính xác phần nào bị kiểm thử fail, nó sẽ cho phép chúng ta dễ dàng xác định được phần thay đổi đã làm gãy chương trình, từ đó, việc sửa lỗi trở nên dễ dàng hơn.


Tôi đã hiểu. Chúng ta thực hiện việc này như thế nào?

Có vô số các framework kiểm thử tự động dành cho PHP chúng ta có thể sử dụng. Một trong những framework kiểm thử được sử dụng rộng rãi nhất là PHPUnit.

PHPUnit là một framework kiểm thử tuyệt vời, nó có thể dễ dàng tích hợp vào trong dự án của các bạn, hoặc các dự án được xây dựng dựa trên các PHP framework phổ thông.

Mặc dù vậy, để đáp ứng cho mục đích của chúng ta, chúng ta sẽ không cần vô vàn tính năng PHPUnit cung cấp. Thay vào đó, chúng ta sẽ tạo ra quy trình kiểm thử của mình bằng một framework kiểm thử đơn giản hơn, được gọi là SimpleTest.

Trong bước tiếp theo, chúng ta sẽ giả sử rằng chúng ta đang phát triển một ứng dụng guestbook (sổ lưu bút), nơi mà mọi người dùng đều có thể thêm và xem các dòng lưu bút. Cứ cho rằng việc thiết kế dao diện đã hoàn tất, và chúng ta chỉ đơn giản xây dựng 1 class chứa application logic của guestbook, nơi ứng dụng thêm và đọc dữ liệu từ cơ sở dữ liệu. Phần đọc dữ liệu của class này là cái chúng ta sẽ phát triển và kiểm thử.


Bước 1. Cài đặt SimpleTest

Có thể nói đây là bước dễ nhất trong tất cả các bước. Thậm chỉ cả anh bạn này cũng có thể thực hiện nó:


Tôi có thể làm điều này... Tôi có thể sử dụng, um... não của tôi!
Hình ảnh hỗ trợ bởi http://longstreet.typepad.com/

Tải SimpleTest tại đây, và giải nén vào 1 thư mục bạn muốn -- tốt nhất là cho vào cùng thư mục với ứng dụng bạn sẽ phát triển, hoặc trong PHP include_path của bạn, cho dễ truy cập.

Cho bài hướng dẫn này, Tôi thiết lập thư mục như sau:

Index.php sẽ thực thi guestbook.php, và gọi phương thức view và hiển thị các thực thể. Bên trong thư mục classes là nơi chúng ta sẽ đặt guestbook.php class, và thư mục kiểm thử, nơi chúng ta đặt thư viện simpletest.


Bước 2. Lên Kế Hoạch Tấn Công

Bước 2, thực sự là một bước quan trọng nhất, để bắt đầu tạo ra mã kiểm thử của bạn. Tại đây, bạn cần lên kế hoạch và nghĩ về việc hàm/phương thức của bạn sẽ làm gì, những dữ liệu đầu vào mà nó có thể sẽ nhận ra sao, các kết quả đầu ra tương ứng nó sẽ gửi đi là gì. Bạn có thể hình dung bước này giống như chơi một ván cờ - bạn cần biết mọi thứ về đối thủ của bạn (chương trình), bao gồm cả mọi điểm yếu (các lỗi tiềm năng), cũng như mọi điểm mạnh (điều gì sẽ xảy ra khi thực thi thành công).

Vì vậy, đối với dụng guestbook của mình, chúng ta hãy thiết lập các sơ đồ:

View

  • Chức năng này sẽ không có bất kỳ dữ liệu đầu vào nào bởi nó sẽ chỉ nhận mọi thứ từ cơ sở dữ liệu và gửi dữ liệu tới máy khách để hiển thị ra.
  • Nó sẽ trả về 1 mảng các bản ghi (record) guestbook, bao gồm tên của người đăng và tin nhắn của anh ta. Nếu không có record nào, nó cần phải trả về một mảng rỗng.
  • Nếu có các record, mảng được trả về sẽ có 1 hoặc nhiều giá trị bên trong nó.
  • Mảng này sẽ có 1 cấu trúc cụ thể, một thứ gì đó như là:

Bước 3. Viết mã kiểm thử!

Giờ, chúng ta có thể viết mã lệnh kiểm thử đầu tiên. Bắt đầu bằng cách tạo ra 1 file tên là guestbook_test.php bên trong thư mục test.

Sau đó, chúng ta chuyển đổi những gì chúng ta mong đợi ở bước 2 thành mã kiểm thử.

Assertions bảo đảm 1 giá trị cần phải đúng như nó nên là - về cơ bản, nó đảm bảo kết quả trả về là những gì bạn mong đợi. Ví dụ, nếu 1 hàm được mong đợi trả về kết quả true khi thực thi thành công, thì trong quá trình kiểm thử, chúng ta nên assert giá trị của nó trả về bằng true.

Như bạn có thể nhìn thấy ở đây, chúng ta kiểm thử view của guestbook trong trường hợp có và không có thực thể dữ liệu. Chúng ta kiểm tra xem ở cả hai hoàn cảnh này, hàm thực thi chương trình có pass các tiêu chuẩn chúng ta đặt ra ở bước 2 hay không. Chắc các bạn cũng thấy là mỗi hàm kiểm thử của chúng ta đều bắt đầu bằng chữ 'test'. Chúng được viết như vậy vì, khi SimpleTest chạy class này, nó sẽ tìm kiếm toàn bộ các hàm bắt đầu với từ 'test' và chạy chúng.

Trong class kiểm thử của mình, chúng tôi cũng sử dụng 1 số phương thức assertion, như là assertTrue, assertIsA, và assertEquals. Phương thức assertTrue kiểm tra 1 giá trị có phải là true hay không. AssertIsA kiểm tra xem 1 biến có thực sự phải là một kiểu hay class xác định nào đó hay không. Cuối cùng, assertEquals kiểm tra xem 1 biến có hoàn toàn bằng với một giá trị cụ thể nào đó hay không.

Có nhiều phương thức assertion khác nhau được cung cấp bởi SimpleTest, như là:

assertTrue($x) Fail nếu $x là false
assertFalse($x) Fail nếu $x là true
assertNull($x) Fail nếu $x đã được thiết lập
assertNotNull($x) Fail nếu $x chưa được thiết lập
assertIsA($x, $t) Fail nếu $x is không phải là class hay kiểu $t
assertNotA($x, $t) Fail nếu $x là class hoặc kiểu $t
assertEqual($x, $y) Fail nếu $x == $y là false
assertNotEqual($x, $y) Fail nếu $x == $y là true
assertWithinMargin($x, $y, $m) Fail nếu abs($x - $y) < $m là false
assertOutsideMargin($x, $y, $m) Fail nếu abs($x - $y) < $m là true
assertIdentical($x, $y) Fail nếu $x == $y là false hoặc là không cùng kiểu dữ liệu
assertNotIdentical($x, $y) Fail nếu $x == $y là true và cùng kiểu dữ liệu
assertReference($x, $y) Fail nếu $x và $y không phải là 2 biến giống nhau
assertClone($x, $y) Fail nếu $x và $y không phải là 2 biến sao chép đồng nhất
assertPattern($p, $x) Fail nếu regex $p không match $x
assertNoPattern($p, $x) Fail nếu regex $p match $x
expectError($x) Fail nếu không có exception mong đợi được ném ra
assert($e) Fail trong trường hợp đối tượng $e không đúng như mong đợi

Danh sách phương thức Assertion được hỗ trợ bởi http://www.simpletest.org/en/unit_test_documentation.html


Bước 4. Thất Bại để Thành Công

Khi bạn đã hoàn tất việc viết mã kiểm thử, bạn cần phải chạy nó. Ở lần đầu tiên bạn chạy kiểm thử, kết quả trả về NÊN LÀ FAIL. Nếu không, điều này đồng nghĩa với việc ứng dụng kiểm thử của bạn không thực sự kiểm tra cái gì cả.

Để chạy kiểm thử, đơn giản bạn chỉ cần chạy guestbook_test.php trên trình duyệt của bạn. Bạn nên nhìn thấy như thế này ở lần đầu:

Điều này xảy ra bởi chúng ta chưa hề tạo ra class guestbook. Để làm điều đó, bạn tạo guestbook.php bên trong thư mục classes. Class này nên chứa các phương thức chúng ta đã lên kế hoạch sử dụng ở trên, nhưng bên trong các phương thức chưa cần chứa bất kỳ thứ gì ở thời điểm này cả. Hãy nhớ, chúng ta đang xây dựng kiểm thử trước khi viết mã lệnh chương trình.

Khi bạn chạy kiểm thử lại lần nữa, nó nên được hiển thị như sau:

Bạn có thể nhìn thấy ở đây, ứng dụng kiểm thử của chúng ta đã trả về fail thành công. Điều này có nghĩa là giờ đây nó đã sẵn sàng để tiếp nhận "hồi âm."


Bước 5. Xây Dựng Mã Chương Trình Hồi Âm Kiểm Thử


Đôi lúc, chúng ta đều cảm thấy như thế này khi chúng ta đang lập trình.
Hình ảnh hỗ trợ bởi http://fermentation.typepad.com/fermentation

Hiện tại chúng ta đã có 1 ứng dụng tự động kiểm thử đi vào hoạt động, chúng ta có thể bắt đầu viết mã lệnh chương trình. Mở class guestbook.php và bắt đầu xây dựng mã lệnh hồi âm cho kiểm thử.

Class guestbook.php này có 1 vài lỗi cố ý được tạo ra, để chúng ta có thể thấy mã chương trình của chúng ta sau khi xây dựng, và chạy kiểm thử fail thì như thế nào.

Khi chúng ta chạy kiểm thử, ta nên nhìn thấy thứ gì đó giống như sau:

Kết quả kiểm thử cho chúng ta thấy trường hợp kiểm thử nào, và assertion nào bị fail. Từ đây, chúng ta có thể dễ dàng nhận thấy dòng 16 và 17 là các assertion đã ném ra lỗi.

Điều này rõ ràng thể hiện cho chúng ta thấy kết quả mảng trả về không có các giá trị đúng đắn như mong đợi. Dựa vào đó, chúng ta sẽ dễ dàng nhận biết phần nào của mã lệnh chương trình đang thực thi sai.

Nào, bây giờ khi chúng ta chạy kiểm thử lại, nó nên hiển thị như sau:


Bước 6. Làm Sạch và Tinh Chỉnh Lại Mã Lệnh

Vì mã lệnh trong ví dụ chúng ta dùng để xây dựng kiểm thử khá là đơn giản, công việc kiểm thử và sửa lỗi không kéo dài quá lâu. Nhưng nếu là một ứng dụng phức tạp, câu chuyện sẽ khác, bạn sẽ phải thực hiện nhiều thay đổi cho mã lệnh của mình, phải làm nó sạch hơn để giúp nó dễ dàng bảo trì và còn nhiều lợi ích khác. Vấn đề ở đây là sự thay đổi thường dễ làm phát sinh các lỗi mới. Đó chính là lúc chúng ta sẽ cần tới kiểm thử tự động - mỗi khi chúng ta có sự thay đổi, chúng ta chỉ cần chạy lại kiểm thử. Nếu nó vẫn pass, thì chúng ta có thể yên tâm rằng chúng ta đã không bẻ gãy điều gì cả. Nếu nó fail, chúng ta biết rằng chúng ta đã gây ra lỗi. Nó sẽ thông báo cho chúng ta biết lỗi đến từ đâu, và hy vọng là cả phương án để sửa nó.


Bước 7. Lặp Lại Quy Trình

Cuối cùng, khi chương trình của bạn cần các chức năng mới, bạn sẽ cần viết thêm mã lệnh kiểm thử mới. Điều đó rất dễ dàng! Lặp lại quy trình từ bước 2 (lúc này SimpleTest đã được thiết lập), và bắt đầu lặp lại tất cả các thủ tục.


Kết luận

Có rất nhiều bài viết chuyên sâu về test-driven develoment ngoài kia, và thậm chí nhiều tính năng của SimpleTest được đề cập tới hơn so với bài viết này - như mock object, stubs, những thứ có thể giúp cho việc xây dựng kiểm thử dễ dàng hơn. Nếu thích đọc thêm, bạn có thể xem tại Wikipedia's test-driven development. Nếu bạn đang quan tâm về việc sử dụng framework kiểm thử SimpleTest, tham khảo thêm tài liệu trực tuyến của nó, hãy thử lướt qua thêm một vài tính năng khác của nó nhé.

Kiểm thử là một phần của chu kỳ phát triển phần mềm, tuy nhiên, nó cũng thường là yếu tố bị lược bỏ đi khi deadline gần kề. Hy vọng rằng, sau khi đọc bài viết này, bạn sẽ nhận thức được giá trị của việc đầu tư vào test-driven development.

Bạn nghĩ gì về Test-Driven Development? Nó có thú vị đối với bạn không, hay bạn cho rằng nó thật tốn thời gian? Cho chúng tôi biết ở phần bình luận bên dưới nhé!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.