Ngày 15 tháng 10 năm 2002.

Lần trước Alphonse và Jerry khởi đầu trên một khung làm việc java đơn giản hỗ trợ dịch vụ socket. Kiểm thử thứ nhất của họ vạch ra trường hợp dồn đuổi (race condition) mà họ đã giải quyết xong. Chuỗi kiểm thử đơn vị hiện tại được trình bày ở Mã dẫn 1 và mã nguồn chính ở Mã dẫn 2.

Mã dẫn 1

Mã dẫn 2

Sau giờ giải lao, chúng tôi trở lại và sẵn sàng tiếp tục với SocketService.

“Chúng ta đã chứng minh được là mình có thể truy cập một lần. Vậy hãy thử truy cập nhiều lần xem sao.” Jerry nói.

“Nghe được lắm.” Tôi trả lời. Sau đó tôi viết một kiểm thử như sau:

“OK, kiểm thử này hỏng.” Tôi nói.

“Nó nên như thế”. Jerry đáp. “Cái SocketService chỉ gọi phương thức accept một lần. Chúng ta cần đặt lời gọi đó vào một vòng lặp.”

“Khi nào vòng lặp đó chấm dứt?” Tôi hỏi.

Jerry nghĩ ngợi một lát và nói: “Khi chúng ta gọi phương thức close của SocketService.”

“Như thế này chăng?” Và tôi điều chỉnh như sau:

Tôi chạy kiểm thử và cả hai đều đạt.

“Tốt.” Tôi nói. “Bây giờ chúng ta có thể truy cập bao nhiêu tùy thích. Không may cái SocketService chẳng làm gì nhiều khi mình truy cập đến nó. Nó chỉ đóng lại mà thôi.”

“Ừa, đổi nó đi.” Jerry nói. “Mình hãy buộc SocketService gởi thông điệp “Hello” mỗi khi chúng ta truy cập đến nó.”

Tôi không quan tâm tới điều đó. Tôi nói: “Tại sao mình làm bẩn SocketService bằng thông điệp “Hello” chỉ để thoả mãn cái kiểm thử của mình? SocketService có thể gửi thông điệp thì tốt nhưng mình không muốn thông điệp này là một phần mã nguồn của SocketService!”

“Ðúng thế!” Jerry đồng ý. “Mình muốn thông điệp được chỉ định và xác thực do cái kiểm thử.”

“Mình làm sao đây?” Tôi hỏi.

Jerry mỉm cười đáp: “Chúng ta dùng mẫu thiết kế Mock Object. Nói một cách ngắn gọn, mình tạo ra một cái interface từ đó SocketService sẽ thao tác sau khi nhận một truy cập. Chúng ta sẽ có kiểm thử ứng dụng cái interface đó dùng để gửi thông điệp “Hello”. Sau đó, mình sẽ có cái kiểm thử dùng để đọc thông điệp từ socket của máy khách (client) và xác thực thông tin được gửi đi một cách đúng đắn.”

Tôi chẳng biết mẫu thiết Mock Object là gì cả và thành phần interface của gã làm tôi bối rối. “Ông chỉ cho tôi được không?” Tôi hỏi.

Thế rồi Jerry vớ lấy bàn phím và bắt đầu gõ.

“Ðầu tiên chúng ta viết cái kiểm thử.”

Tôi kiểm tra đoạn mã này cẩn thận. “OK, ông tạo ra cái gọi là HelloServer và đưa nó vào trong phương thứcserve. Cái này sẽ làm hỏng hết các kiểm thử khác!”

“Hay lắm!” Jerry thốt lên. “Ðiều đó có nghĩa là chúng ta cần tái cấu trúc những kiểm thử khác trước khi tiếp tục.”

“Nhưng các dịch vụ trong hai kiểm thử kia chẳng làm gì hết.” Tôi ý kiến.

“Tất nhiên là chúng có làm gì đó – chúng đếm số truy cập! Mày có nhớ là mày ghét mấy cái biến số truy cập đến thế nào không, và nó chỉ là phần phụ mà thôi? Bây giờ mình sẽ dẹp chúng đi.”

“Mình sắp sửa làm thế à?”

“Xem đây.” Jerry cười rộ. “Ðầu tiên chúng ta đổi hai cái kiểm thử và thêm biến số connections vào kiểm thử.”

“Kế tiếp mình tạo cái interface.”

“Sau đó chúng ta tạo biến số connectionCounter và khởi tạo nó trong hàm tạo của TestSocketServer bằng một lớp lồng nặc danh để nó tăng cấp biến số connections.

“Cuối cùng, chúng ta làm chúng có thể biên dịch trọn bộ bằng cách thêm đối số phụ vào phương thức serve của SocketService và chuyển cái kiểm thử mới thành chú thích (để nó khỏi chạy).”

“OK, tôi biết ý ông rồi.” Tôi nói. “Hai cái kiểm thử cũ lúc này hẳn phải hỏng bởi lẽ SocketService không bao giờ gọi phương thức serve của đối số SocketServer của nó.”

Tất nhiên các kiểm thử đã hỏng vì chính lý do ấy.

Tôi biết phải làm gì tiếp. Tôi vớ lấy bàn phím và thay đổi như sau:

Ðoạn mã này làm các kiểm thử chạy được.

“Hay lắm!” Jerry nói. “Bây giờ chúng ta phải làm cho cái kiểm thử mới chạy.”

Thế nên tôi bỏ chú thích cho đoạn kiểm thử và biên dịch nó. Nó “la làng” trong phần HelloServer.

“Ô, đúng rồi. Mình phải cài đặt HelloServer. Nó sẽ phun ra chữ “hello” từ socket, phải không?”

“Ðúng thế.” Jerry xác nhận.

Thế rồi tôi viết lớp  mới trong tệp TestSocketServer.java như sau

Mọi kiểm thử đều ổn.

“Cũng dễ thôi.” Jerry nói.

“Ừa. Phần mẫu thiết kế Mock Oject khá hữu dụng. Nó cho phép ta duy trì các mã dùng để kiểm thử trong kế hoạch kiểm thử. SocketService không biết gì cả.”

“Còn hữu dụng hơn thế.” Jerry trả lời. “Các server thật cũng sẽ ứng dụng interface SocketServer.”

“Tôi biết.” Tôi trả lời. “Thật lý thú khi thấy từ nhu cầu tạo ra một kiểm thử đơn vị đưa mình đến chỗ tạo ra một thiết kế hữu dụng một cách tổng quát.”

“Ðiều này thường xảy ra mà.” Jerry nói. “Kiểm thử là người dùng đó. Nhu cầu dùng kiểm thử thường trùng hợp với nhu cầu của người dùng thật sự.”

“Nhưng tại sao lại gọi nó là Mock Object?”

“Hãy nghĩ trên phương diện thế này. HelloServer dùng để thay thế cho, hoặc là một bản nháp, của một server thật. Cái mẫu thiết kế này cho phép chúng ta thay thế bản nháp của chuyện kiểm thử vào mã nguồn ứng dụng thật.”

“À ra vậy.” Tôi đáp. “Thôi thì bây giờ mình nên dọn dẹp phần mã này và xoá bỏ cái biến số truy cập vô dụng kia vậy.”

“Ðồng ý.”

Thế rồi chúng tôi dọn dẹp thêm một chút nữa và nghỉ giải lao. Kết quả của SocketService như sau:

Bài tiếp: Thợ lành nghề #8: Kiểm thử là một dạng tài liệu (Dịch vụ Socket 3)

Bài trước: Thợ lành nghề #6: Một lần không đủ (Dịch vụ Socket 1)

Tác giả: Robert C. Martin

Người dịch: Hoàng Ngọc Diêu (conmale)


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.