Clean Coder – Chương 8 – Các Chiến Thuật Kiểm Tra

Những lập trình viên chuyên nghiệp đều tự kiểm tra code của họ. Nhưng việc kiểm tra không chỉ đơn giản là việc viết một vài bài unit test hoặc một vài bài acceptance test. Viết những bài test này là điều tốt nhưng còn xa mới gọi là đủ. Điều mà mỗi nhóm phát triển phần mềm chuyên nghiệp cần là một chiến thuật kiểm tra tốt.

Vào năm 1989, tôi đang làm việc tại Rational trong lần ban hành đầu tiên của Rose. Gần như mỗi tháng, quản lý nhóm QA của chúng tôi sẽ kêu gọi một ngày “Săn Bug” (Bug Hunt). Mỗi người trong nhóm, từ lập trình viên cho tới quản lý, cho tới thư ký, cho tới người quản trị dữ liệu, sẽ ngồi xuống với Rose và cố gắng để làm cho nó gặp lỗi. Phần thưởng sẽ được thưởng cho các loại bug khác nhau. Người nào mà tìm được một bug gây sập phần mềm có thể thắng được một bữa tối dành cho hai người. Người nào mà tìm được nhiều lỗi nhất có thể thắng được một kỳ nghỉ cuối tuần ở Monterrey.

QA nên không tìm thấy gì cả

Tôi đã nói điều này trước đây, và bây giờ tôi sẽ nói lại lần nữa. Mặc dù sự thật là công ty của bạn có thể có một nhóm QA riêng để kiểm tra phần mềm, thì mục tiêu của nhóm phát triển phần mềm cần phải đặt ra là để cho nhóm QA không tìm thấy điều gì sai cả.

Dĩ nhiên, mục tiêu này có vẻ sẽ không thể đạt được liên tục. Nhưng dù gì, khi bạn có một nhóm những người thông minh xung quanh và đã xác định là tìm tất cả các sai sót và lỗi trong một sản phẩm, thì họ nhiều khả năng sẽ tìm thấy được. Mỗi lần nhóm QA tìm thấy điều gì sai thì nhóm phát triển phần mềm nên cảm thấy điều đó thật kinh khủng. Họ nên tự hỏi chính mình tại sao nó lại xảy ra và tìm cách để ngăn chặn lỗi lặp lại lần nữa trong tương lai.

QA là một phần của nhóm

Phần trước tôi mô tả có vẻ như nhóm QA và nhóm Phát Triển tách biệt với nhau, mối quan hệ của họ có vẻ như xung đột lẫn nhau. Nhưng đó không phải là ý định của tôi. Thậm chí, nhóm QA và nhóm Phát Triển cần phải làm việc với nhau để đảm bảo chất lượng của hệ thống. Vai trò tốt nhất của QA trong nhóm đó là người tiêu chuẩn hóa và mô tả đặc điểm của hệ thống.

QA là người tiêu chuẩn hóa hệ thống

Vai trò của QA phải là làm việc với khách hàng để tạo ra những bài acceptance test tự động, thứ mà sẽ thực sự trở thành tài liệu yêu cầu và tiêu chuẩn của toàn bộ hệ thống. Họ liên tục thu thập những yêu cầu từ khách hàng và biên dịch chúng trở thành những bài test để mô tả cho những lập trình viên cách mà hệ thống cần phải hoạt động như thế nào (Xem Chương 7, “Acceptance Test”). Nói chung thì khách hàng viết các bài test “happy path”, trong khi QA thì viết các bài test góc, giới hạn và “unhappy path”.

QA là người mô tả đặc điểm hệ thống

Vai trò khác của QA là dùng nguyên lý kiểm tra thăm dò (exploratory testing[1]) để mô tả đặc điểm hành vi thực sự của hệ thống đang chạy và báo cáo hành vi đó trở lại cho nhóm Phát Triển và khách hàng. Trong vai trò này QA không phải biên dịch các yêu cầu hệ thống sang các bài acceptance test. Đúng hơn là họ xác định các hành vi thực sự của cả hệ thống.

Kim tự tháp kiểm tra tự động

Những lập trình viên chuyên nghiệp sử dụng các nguyên lý TDD để tạo ra các bài unit test. Những nhóm phát triển phần mềm chuyên nghiệp dùng các bài acceptance test để thể hiện rõ hệ thống của họ, và hệ thống tích hợp liên tục CI (Chương 7) được dùng để ngăn chặn lỗi phát sinh. Nhưng những bài test này chỉ là một phần của câu truyện. Cũng tốt như khi chúng ta có một bộ các bài unit test và acceptance test, chúng ta cần các bài test ở cấp cao hơn để đảm bảo rằng QA không tìm thấy lỗi nào cả. Hình 8-1 chỉ ra Kim tự tháp kiểm tra tự động, một hình thể hiện các loại test mà một tổ chức phát triển phần mềm chuyên nghiệp đều cần.

Hình 8-1. Kim tự tháp kiểm tra tự động

Unit test

Tại đáy của kim tự tháp là các bài unit test. Những bài test này được viết bởi những lập trình viên, dành cho lập trình viên, bằng ngôn ngữ lập trình của hệ thống. Mục đích của những bài test này là mô tả đặc điểm hệ thống ở mức thấp nhất. Những lập trình viên viết những bài test này trước khi viết code chương trình như là cách để mô tả thứ mà họ sẽ viết. Chúng được thực thi như là một phần của hệ thống CI để đảm bảo rằng mục đích của các lập trình viên được đảm bảo.

Các bài unit test cung cấp độ phủ source code gần như 100% là khả thi. Thông thường con số này ở vào khoảng hơn 90%. Và đó phải là độ phủ thực tế chứ không phải là những bài test thực thi code mà không kiểm tra hoạt động của code.

Component test

Có một vài acceptance test được đề cập ở chương trước. Thông thường chúng được viết dựa theo những component riêng lẻ của hệ thống. Component của hệ thống đóng gói các logic hoạt động của hệ thống, vì vậy các bài test cho những component này chính là các bài acceptance test đối với logic hoạt động của hệ thống.

Như thể hiện ở Hình 8-2, một bài component test gói gọn một component. Nó đưa dữ liệu đầu vào vào bên trong component và thu thập dữ liệu đầu ra từ component đó. Nó kiểm tra xem dữ liệu đầu ra xem có khớp với dữ liệu đầu vào không. Bất cứ một component hệ thống nào khác đều phải được tách rời khỏi những bài test này bằng những kỹ thuật mock[2] và test-double[3] thích hợp.

Hình 8-2. Component test

Component test được viết bởi QA và khách hàng cùng với sự hỗ trợ của nhóm Phát Triển. Chúng được soạn ra trong một môi trường kiểm tra component như FitNesse, JBehave, hay Cucumber. (Các GUI component được kiểm tra với các môi trường kiểm tra GUI như Selenium hay Watir) Mục đích là làm sao cho khách hàng phải có thể đọc và hiểu được những bài test này, thậm chí còn tự viết được chúng.

Component test có độ phủ gần một nửa hệ thống. Chúng hướng nhiều hơn tới các tình huống “happy path” và những trường hợp giới hạn biên, góc rất hiển nhiên. Phần lớn các trường hợp “unhappy path” được kiểm tra bằng các bài unit test và chúng vô nghĩa ở cấp độ component test.

Integration test

Những bài test này chỉ có ý nghĩa đối với những hệ thống lớn mà có nhiều component. Như bạn thấy ở Hình 8-3, những bài test này được ghép thành những nhóm component và kiểm tra xem chúng giao tiếp với nhau có tốt không. Các component khác của hệ thống sẽ được tách rời như thường lệ bằng các kỹ thuật mock và test-double thích hợp.

Các bài integration test là cả một nghệ thuật sắp xếp. Chúng không kiểm tra logic hoạt động của hệ thống, mà chúng kiểm tra việc ghép nối giữa các component có tốt không. Chúng là những bài kiểm tra ghép nối đảm bảo rằng các component được kết nối chính xác và có thể giao tiếp rõ ràng với nhau.

Hình 8-3. Integration test

Các bài integration test thường được viết bởi các kiến trúc sư thiết kế hệ thống, hoặc thiết kế trưởng của hệ thống. Những bài test này đảm bảo rằng kiến trúc của hệ thống được làm theo đúng thiết kế. Ở cấp độ test này, chúng ta có thể thấy một số bài test về hiệu năng và tốc độ của phần mềm.

Integration test thường được viết với cùng ngôn ngữ và môi trường của component test. Chúng thường không được bao gồm khi chạy bộ Tích Hợp Liên Tục CI, do chúng thường mất nhiều thời gian để chạy. Thay vào đó, những bài test nay được chạy định kỳ (hằng đêm, hằng tuần…) tùy theo mức độ cần thiết mà người viết test đưa ra.

System test

Đây là những bài test tự động thực thi kiểm tra lại toàn bộ hệ thống đã được tích hợp. Chúng chính là những bài integration test cuối cùng. Chúng không kiểm tra logic hoạt động của hệ thống trực tiếp, mà chúng kiểm tra xem hệ thống đã được kết nối chính xác và các phần của hệ thống có tương tác đúng như kế hoạch không. Chúng ta sẽ thấy những bài test hiệu năng và tốc độ trong bộ test này.

Những bài test này được viết bởi các kiến trúc sư thiết kế hệ thống và các trưởng nhóm kỹ thuật. Chúng thường được viết với cùng ngôn ngữ và môi trường như các bài integration test của UI. Chúng được thực hiện tương đối không thường xuyên tùy theo thời gian để chạy bộ test này, nhưng nếu càng thường xuyên thì càng tốt.

Các bài system test có độ phủ gần 10% hệ thống. Đó là do mục đích của chúng không phải là đảm bảo hệ thống hoạt động chính xác, mà là cấu trúc của hệ thống chính xác. Hoạt động chính xác của code bên dưới và các component đã được đảm bảo bởi các lớp kiểm tra thấp hơn của kim tự tháp kiểm tra.

Các bài exploratory test thủ công

Đây là những bài test cần con người đặt tay vào bàn phím và mắt nhìn vào màn hình. Những bài test này không được thực hiện tự động, cũng không phải là dạng test theo kịch bản. Mục đích của những bài test này là để khám phá những hành vi không mong muốn của hệ thống trong khi vừa xác nhận lại những hành vi đã được dự tính trước. Để đạt được mục tiêu đó, chúng ta cần có bộ não con người, kèm với sự sáng tạo, làm việc để nghiên cứu và khám phá hệ thống. Việc tạo ra một bản kế hoạch test được viết sẵn đối với loại test này thì sẽ làm hỏng mục đích của nó.

Một vài nhóm sẽ có những chuyên gia làm công việc này. Một số nhóm khác sẽ đơn giản dành một hoặc hai ngày để “săn bug” với số lượng người tham gia nhiều nhất có thể, bao gồm cả các quản lý, các thư ký, các lập trình viên, các người kiểm tra, và các nhân viên viết tài liệu kỹ thuật, “tấn công” vào hệ thống để xem họ có thể làm hỏng nó được không.

Mục tiêu của loại test này không phải là độ phủ. Chúng ta sẽ không chứng minh được bất cứ logic hoạt động nào của hệ thống hoặc bất cứ nhánh thực thi nào bằng các bài test này. Mục tiêu ở đây là đảm bảo rằng hệ thống hoạt động tốt dưới sự vận hành của con người và tìm kiếm một cách sáng tạo những trường hợp “đặc biệt” nhiều nhất có có thể.

Kết luận

TDD là nguyên tắc làm việc mạnh mẽ, và các bài Acceptance Test là những cách có giá trị để thể hiện và thực hiện những yêu cầu của hệ thống. Nhưng chúng cũng chỉ là một phần của chiến thuật kiểm tra tổng thể. Để làm tốt được mục tiêu “QA không tìm thấy gì hết”, thì các nhóm phát triển phần mềm cần làm việc mật thiết với nhóm QA để tạo ra một hệ thống các bài unit, component, integration, system, và exploratory test. Những bài test này cần phải được chạy càng thường xuyên càng tốt để cung cấp phản hồi tối đa cho các lập trình viên và để đảm bảo rằng hệ thống luôn duy trì ở trạng thái sạch sẽ.

Tham khảo

[COHN09]: Mike Cohn, Succeeding with Agile, Boston, MA: Addison-Wesley, 2009.


[1] http://www.satisfice.com/articles/what_is_et.shtml

[2] mock: chương trình mô phỏng đầu ra một hàm bằng cách đặt sẵn đầu ra mong muốn.

[3] test-double: kiểm tra nhân đôi, là thuật ngữ chung chỉ đối tượng mô phỏng những phụ thuộc của hàm cần kiểm tra. Trong test-double có nhiều loại như stub, mock…

Trả lời

Email của bạn sẽ không được hiển thị công khai.