Lilotech

Ba định luật TDD

                                       

Ba định luật TDD

 

Là một lập trình viên, bạn biết gì về ba định luật TDD (Test-Driven Development)? Hãy cùng PT-Tech tìm hiểu qua bài viết sau.

 

Đây là ba định luật TDD được miêu tả theo cách của Uncle Bob-Robert C. Martin, một lập trình viên có tiếng, người gốc Mỹ, vào năm 2005 trên trang butUncleBob. Nói theo cách của chú Bob:

Trong những năm qua, tôi đã mô tả Phát triển hướng thử nghiệm (TDD) theo ba quy tắc đơn giản. Chúng bao gồm:

Bạn không được phép viết bất kỳ mã sản xuất nào khi nó không qua được kiểm thử mức đơn vị (unit test).
Bạn không được phép viết thêm vào khi kiểm thử đơn vị thất bại; vì dịch mã thất bại chính là thất bại.
Bạn không được phép viết thêm bất kỳ mã sản xuất nào mà đã thất bại trong một kiểm thử đơn vị.

 

Bạn phải bắt đầu bằng cách viết một kiểm thử mức đơn vị cho chức năng mà bạn định viết. Nhưng theo quy tắc 2, bạn không thể viết nhiều kiểm thử đơn vị. Ngay khi kiểm thử đơn vị không biên dịch được hoặc không xác nhận, bạn phải dừng và viết mã sản xuất. Và theo quy tắc 3, bạn chỉ có thể viết mã sản xuất mà kiểm thử đơn vị biên dịch  được hoặc thông qua.

 

Nếu bạn ngẫm nghĩ một chút, bạn sẽ nhận ra đơn giản là bạn không thể viết nhiều mã khi mà chúng không cần biên dịch hay thực thi một điều gì cả. Đó là một lý do. Trong tất cả mọi thứ chúng ta làm, cho dù viết kiểm thử, viết mã sản xuất hay tái cấu trúc, chúng ta luôn giữ cho hệ thống luôn hoạt động. Thời gian giữa các lần chạy thử tính theo thứ tự giây hoặc phút. Thậm chí 10 phút đã là quá dài.

 

Để thấy điều này hoạt động thế nào, hãy xem Trò chơi Bowling Kata .

 

Bây giờ, hầu hết các lập trình viên, khi lần đầu tiên nghe về kỹ thuật này, đã nghĩ: " Điều này thật ngu ngốc! " " Nó sẽ làm tôi chậm lại, thật lãng phí thời gian và công sức. Nó sẽ kéo tôi ra khỏi suy nghĩ, thiết kế của mình và nó sẽ phá vỡ guồng quay của tôi". Tuy nhiên, hãy nghĩ về những gì sẽ xảy ra nếu bạn bước vào một căn phòng đầy những người làm việc theo cách tôi nói. Hãy chọn người ngẫu nhiên tại thời điểm bất kỳ. Và một phút trước, tất cả mã của họ đã hoạt động.

 

Hãy để tôi nhắc lại rằng: Một phút trước tất cả mã của họ đã hoạt động! Và nó không quan trọng bạn chọn ai, và nó không quan trọng khi bạn chọn. Một phút trước tất cả mã của họ đã làm việc!

Nếu tất cả mã của bạn hoạt động mỗi phút, bạn có thường xuyên sử dụng trình gỡ lỗi không? Trả lời: không thường xuyên lắm. Đơn giản hơn là chỉ cần nhấn ^ Z một loạt lần để đưa mã trở lại trạng thái làm việc, và sau đó cố gắng viết lại để chúng có thể hoạt động. Nếu bạn không thường gỡ lỗi, bạn sẽ tiết kiệm được bao nhiêu thời gian? Bạn dành bao nhiêu thời gian để gỡ lỗi? Bạn dành bao nhiêu thời gian để sửa lỗi sau khi bạn gỡ lỗi chúng? Điều gì nếu bạn có thể giảm thời gian đó xuống một phần đáng kể?

 

Nếu bạn làm việc theo cách tôi nói, thì mỗi giờ bạn sẽ tạo ra một vài kiểm thử. Mỗi ngày có hàng chục kiểm thử. Mỗi tháng có hàng trăm kiểm thử. Trong suốt một năm, bạn sẽ viết hàng ngàn kiểm thử. Bạn có thể giữ tất cả các kiểm thử này và chạy chúng bất cứ lúc nào bạn muốn! Khi nào bạn sẽ chạy chúng? Mọi lúc! Bất cứ lúc nào bạn thực hiện một thay đổi nào đó!

 

Tại sao chúng ta không dọn dẹp mã mà chúng ta biết là lộn xộn? Vì chúng ta sợ sẽ phá vỡ chúng. Nhưng nếu có các kiểm thử, chúng ta có thể chắc chắn rằng mã không bị phá vỡ, hoặc chúng ta sẽ phát hiện ra sự cố ngay lập tức. Nếu có các kiểm thử, chúng ta sẽ không sợ thực hiện thay đổi. Nếu có mã lộn xộn, hoặc một cấu trúc bừa bộn, chúng ta có thể dọn dẹp nó mà không sợ hãi. Do có các kiểm thử, mã trở nên dễ uốn nắn trở lại. Do có các kiểm thử, phần mềm sẽ trở nên mềm trở lại.

Chân dung của chú Bob (Nguồn: cleancoders)

 

Nhưng lợi ích vượt xa điều đó. Nếu bạn muốn biết cách gọi một API nhất định, thì đã có một kiểm thử làm điều đó. Nếu bạn muốn biết làm thế nào để tạo một đối tượng nhất định, thì đã có một kiểm thử thực hiện điều đó. Bất cứ điều gì bạn muốn biết về hệ thống hiện có, đã có một kiểm thử miêu tả nó. Các kiểm thử giống như các tài liệu thiết kế nhỏ, các ví dụ mã hóa nhỏ mô tả cách hệ thống hoạt động và cách sử dụng nó.

 

Bạn đã từng tích hợp một thư viện bên thứ ba vào dự án của bạn chưa? Nếu cần, bạn đã có một hướng dẫn sử dụng lớn với đầy đủ tài liệu hữu ích. Ngoài ra, bạn còn có một phụ lục mỏng về các ví dụ. Bạn đã đọc cái nào trong hai cái đó chưa? Các kiểm thử là các ví dụ trong suốt quá trình làm của bạn! Chúng là phần hữu ích nhất của tài liệu hướng dẫn sử dụng trên. Chúng là những ví dụ sống động về cách sử dụng mã. Chúng là các tài liệu thiết kế chi tiết đến rợn người, hoàn toàn rõ ràng, và chúng chính thức đến mức chúng thực thi và không thể tách rời mã sản xuất.

 

Nhưng lợi ích còn vượt xa điều đó nữa. Nếu bạn đã từng thử thêm các kiểm thử đơn vị vào một hệ thống đã hoạt động, có lẽ bạn thấy rằng điều đó không vui chút nào. Bạn phải thay đổi các phần của thiết kế hệ thống hoặc gian lận các kiểm thử; bởi vì hệ thống mà bạn đang cố viết kiểm thử cho không được thiết kế để có thể thử kiểm. Ví dụ: bạn muốn kiểm tra một số chức năng 'f'. Tuy nhiên, 'f' gọi một chức năng khác xóa bản ghi khỏi cơ sở dữ liệu. Trong thử kiểm của bạn, bạn không muốn xóa hồ sơ, nhưng bạn không có cách nào để ngăn chặn nó. Đó là hệ thống không được thiết kế để thử kiểm.

 

Khi bạn tuân theo ba quy tắc của TDD, tất cả mã của bạn sẽ được kiểm tra theo đúng nghĩa! Và một từ khác đồng nghĩa với "testable" (có thể thử kiểm) là "decoupled" (tách ra). Để kiểm tra một module riêng lẻ, bạn phải tách nó ra. Có thể nói, TDD buộc bạn phải tách các module. Nếu bạn tuân theo ba quy tắc, bạn sẽ thấy mình phải làm việc tách rời nhiều hơn bạn từng làm. Điều này buộc bạn phải tạo ra các thiết kế tốt hơn, ít ghép nối hơn.

 

Với tất cả những lợi ích này, các quy tắc nhỏ ngu ngốc của TDD có thể không thực sự ngu ngốc. Chúng có thể là một cái gì đó cơ bản, cũng có thể là một cái gì đó rất sâu sắc. Thực sự, tôi đã là một lập trình viên trong gần ba mươi năm trước khi tôi được biết về TDD. Nhưng  trong ba mươi năm kinh nghiệm đó, không ai  dạy tôi một thao tác lập trình cấp thấp như vậy sẽ tạo ra sự khác biệt lớn đến thế. Nhưng khi tôi bắt đầu sử dụng TDD, tôi đã chết lặng về hiệu quả của kỹ thuật này. Tôi thực sự bị cuốn hút. Tôi không thể tưởng tượng việc ngồi gõ một lô mã lớn rồi hy vọng rồi nó sẽ hoạt động nữa. Tôi không thể chịu được việc tách cả lô module ra, ráp lại và hy vọng chúng vẫn có thể chạy được trước thứ Sáu tới. Khi lập trình, mỗi quyết định của tôi đều được thcu1 đẩy bởi điều cơ bản được thực hiện lại một phút kể từ bây giờ này (là tạo lại kiểm thử đơn vị từng phút ngay từ bây giờ).

 

Thông qua các chia sẻ của chú Bob, hy vọng bạn sẽ có lựa chọn phù hợp với bản thân.

Yến Nhi dịch và tổng hợp