“Best Practice” cần thiết khi viết TDD

Chào mọi người! Trong bài viết này tôi xin trình bày những “Best Practices” cần biết khi phát triển TDD.

“Best Practice” là tập hợp những kỹ thuật và cách làm mà đã được nghiên cứu, chứng minh trong thực tế, khi áp dụng nó sẽ làm cho sản phẩm của chúng ta tốt hơn, tránh được rất nhiều các vấn đề mà người khác đã gặp phải.

“Best Practice” cho TDD sẽ tập hợp các quy tắc mà chúng ta nên áp dụng khi viết TDD. Cụ thể ở đây là tôi giới thiệu việc viết TDD trong Java và Scala, nhưng thực ra chúng có thể áp dụng trong hầu hết các ngôn ngữ. Tôi sẽ trình bầy các quy tắc theo số thứ tự để mọi người dễ nhớ.

Các quy tắc dùng trong phát triển TDD

1. Tách biệt giữa phần test và code

Ưu điểm: Giúp quản lý code dễ dàng hơn, tách biệt giữa phần code và test

Chúng ta cần có ít nhất 2 folder một cho code và một cho test. Ví dụ trong scala + play2 có thể đặt code trong project/app, test trong project/test. Ngoài việc giúp quản lý code dễ dàng hơn, đây còn là yêu cầu của rất nhiều công cụ quản lý source code

2. Tên package giống nhau giữa test code & source code

Ưu điểm: Giúp tìm test code dễ dàng hơn

Khi test code được tổ chức giống như source code sẽ giúp tìm test code dễ dàng hơn và ngược lại. Điều này rất có ý nghĩa trong các dự án lớn với hàng trăm file code & test code.

3. Đặt tên class test dựa trên file code

Ưu điểm: Tìm file test code dễ hơn, đặc biệt là khi cần tim file test cho một file code cụ thể.

Trong các dự án lớn, số lượng các file code rất lớn. Khi đó nếu các file test không được đặt tên theo một quy tắc nào đó sẽ rất khó tìm file test code. Để giải quyết vấn đề này thì một quy tắc hay được dùng là, đặt tên class test bao gồm tên class cần test và hậu tố “Test” hoặc “Spec” ở cuối. Ví dụ ta có class cần được viết test là ApplicationController.scala, class test sẽ là ApplicationControllerTest.scala hoặc ApplicationControllerSpec.Scala

4. Đặt tên phương thức test mô tả đầy đủ ý nghĩa

Ưu điểm: Giúp hiểu đầy đủ ý nghĩa của phương thức test mà không cần xem comment

Ta không nên dựa vào comment để cung cấp thông tin cho phương thức test. Bởi vì comment không xuất hiện khi test chạy, và cũng không xuất hiện trong các report bởi các tool. Do đó ta nên đặt tên phương thức đầy đủ ý nghĩa.

Có rất nhiều cách để đặt tên test method. Nhưng cách được ưa chuộng là đặt tên dùng Given/When/Then trong khái niệm của BDD. Given – đưa ra điều kiện, When – miêu tả hành động, Then – mô tả kết quả mong đợi. Nếu một số test không có điều kiện trước, thì Given có thể bỏ qua

Bên dưới là một ví dụ của đặt tên:

@Test
public final void whenSemicolonDelimiterIsSpecifiedThenItIsUsedToSeparateNumbers() {

}

5. Viết test trước khi viết code

Ưu điểm: Đảm bảo rằng test code luôn được viết

Bằng cách viết hoặc sửa đổi test code trước khi viết, người phát triển sẽ tập trung hơn vào yêu cầu trước khi bắt đầu code. Điều này là điểm khác biệt lớn nhất so với việc viết test sau khi viết code. Hơn nữa việc viết test trước giúp chúng ta tăng chất lượng của test code, tránh vấn đề viết test một cách qua loa.

6. Chạy tất cả các test mỗi lần thay đổi code

Ưu điểm: Đảm bảo rằng không có lỗi xảy ra khi do thay đổi code

Mỗi lần thay đổi bất kỳ phần nào trong code, dù lớn hay nhỏ, ta cũng cần chạy lại tất cả các test. Một cách lý tưởng thì các test có thể chạy nhanh ngay trên máy của người phát triển để họ không phải đợi quá lâu. Mỗi khi code được đưa lên git hoặc svn, cần chạy các test lại để đảm bảo rằng không có vấn đề gây ra do merge code. Điều này đặc biệt quan trọng khi có nhiều người cùng tham gia phát triển code. Có thể setup công việc này một cách tự động dùng các phần mềm như Jenkins, Hudson để setup trên client

7. Kiểm tra code(Refactor) chỉ sau khi tất cả test đã thành công

Ưu điểm: quá trình kiểm tra code sẽ an toàn, không bị kiểm tra các phần code bị lỗi

Nếu tất cả test chạy thành công thì nó tương đối an toàn để kiểm tra code.  Sau khi refactor code thì không cần phải viết thêm test mới, nhưng sẽ cần sửa đổi test code cho những phần đã thay đổi.  Điều kiện lý tưởng là, sau khi refactor thì tất cả các test chạy đều thành công

8. Hạn chế sự phụ thuộc giữa các test

Ưu điểm: Test có thể chạy độc lập mà không phụ thuộc vào các test khác

Mỗi test nên độc lập từ các test khác. Người phát triển nên có thể thực thi bất kỳ phương thức test nào đó, hoặc một tập các test độc lập. Nếu có sự phụ thuộc từ các test thì chúng dễ bị ảnh hưởng khi viết các test mới.

9. Các test nên chạy nhanh

Ưu điểm: Các test được dùng thường xuyên, nên test chạy nhanh sẽ tích kiệm thời gian

Nếu test chạy mất nhiều thời gian, người phát triển sẽ dễ dàng dừng chúng hoặc chỉ chạy một tập nhỏ của các test liên quan tới phần họ sắp thay đổi. Rõ ràng điều này là không tốt vì không thể đảm bảo tất cả các test sẽ thành công khi viết test mới hoặc code mới. Lợi ích từ chạy nhanh là bên cạnh việc tích kiệm thời gian, nó còn giúp phát hiện các vấn đề sớm và có thể giải quyết vấn đề sớm.

10. Dùng các đối tượng dữ liệu giả (mocks)

Ưu điểm: Giảm sự phụ thuộc vào code, test chạy nhanh hơn

Mock là điều kiện cần thiết để làm cho các test nhanh hơn bời vì phương thức test không cần kết nối và đợi lấy dữ liệu từ các đối tượng bên ngoài, tất cả dữ liệu cần thiết đã được cung cấp trong đối tượng mock. Nó cũng giúp người phát triển tập trung hơn vào phần test mà họ đang viết không cần quan tâm đến các đối tượng cung cấp dữ liệu. Có rất nhiều công cụ hỗ trợ tạo ra các đội trượng mock khi viết unit test, ví dụ như: mockito, …

11. Dùng setup và tear-down phương thức

Ưu điểm: Cho phép setup dữ liệu trước khi chạy test và reset dữ liệu sau khi test chạy xong

Phương thức setup cho phép cài đặt dữ liệu trước khi chạy unit test, tear-down để reset dữ liệu sau khi test chạy xong. Điều này đảm bảo các test không bị phụ thuộc lẫn nhau. Dữ liệu sinh ra do test sẽ được reset lại sau khi nó chạy xong.

12. Hạn chế dùng base class

Ưu điểm: Làm cho class test rõ ràng

Class test cần rõ ràng và mạch lạc, dó đó chúng ta nên hạn chế dùng base class khi viết unit test.

13. Dùng các công cụ để hỗ trợ test

Ưu điểm: Đo các thông số liên quan đến test, thực hiện test một cách tự động

Code Coverage: Là công cụ đo phần trăm code được test trong dự án. Có thể dùng một trong các tool như SonarQuebe, Clover. Các công cụ này hỗ trợ hầu hết các ngôn ngữ

CI – Continuous integration (CI): Tool để thực hiện các thao tác tự động theo một kế hoạch lập từ trước đó. Có thể dùng các công cụ như: Jenkins, Hudson

SEPTENI TECHNOLOGY

Add a Comment

Scroll Up