2010-06-30 43 views
6

Tôi có một đối tượng dịch vụ không phát triển được phát triển với TDD. Nó bắt đầu với một nhiệm vụ đơn giản: Đối với một đối tượng từ hàng đợi, hãy xây dựng một nỗ lực để xử lý không đồng bộ. Vì vậy, tôi đã viết một bài kiểm tra xung quanh phương pháp constructAttempt() tôi:TDD - Tái cấu trúc thành hộp đen?

void constructAttempt() {...} 

Có rất nhiều tình huống có thể là cần phải được xem xét, vì vậy tôi có một tá các xét nghiệm cho phương pháp này.


Sau đó, tôi đã thực hiện những gì tôi thực sự cần để làm: Quét toàn bộ hàng đợi và tạo một loạt các lần thử. Vì vậy, mã trông giống như:

public void go() { 
    for (QueuedItem item : getQueuedItems()) { 
     constructAttempt(item); 
    } 
} 

Vì vậy, tôi đã thêm một hoặc hai thử nghiệm mới cho phương pháp go() này.


Cuối cùng tôi phát hiện ra tôi cần một số tiền xử lý đôi khi có thể ảnh hưởng đến constructAttempt(). Bây giờ mã trông giống như:

public void go() { 
    preprocess(); 
    for (QueuedItem item : getQueuedItems()) { 
     constructAttempt(item); 
    } 
} 

tôi có một vài nghi ngờ về những gì tôi nên làm gì bây giờ.

Tôi có giữ mã như cũ, với constructAttempt(), preprocess()go() thử nghiệm độc lập không? Tại sao có/tại sao không? Tôi có nguy cơ không bao gồm các tác dụng phụ của tiền xử lý và phá vỡ đóng gói.

Hoặc tôi có nên cấu trúc lại toàn bộ bộ thử nghiệm của mình để chỉ gọi go() (phương pháp công khai duy nhất)? Tại sao có/tại sao không? Điều này sẽ làm cho các bài kiểm tra hơi mơ hồ hơn một chút, nhưng mặt khác nó sẽ đưa tất cả các tương tác có thể vào xem xét. Thực tế nó sẽ trở thành một thử nghiệm hộp đen chỉ sử dụng API công khai, những gì có thể không phù hợp với TDD.

+0

Nếu bạn cấu trúc lại bộ thử nghiệm của mình để chỉ kiểm tra phương thức go() ... bạn sẽ xác định như thế nào nếu các lỗi được phát hiện có trên preprocess() hoặc constructAttempt ?? –

Trả lời

6

Phương pháp go thực sự chỉ phối hợp một số tương tác và không phải là điều rất thú vị theo đúng nghĩa của nó. Nếu bạn viết các bài kiểm tra của mình dựa trên go thay vì các phương pháp cấp dưới của bạn, các bài kiểm tra có thể phức tạp một cách phức tạp bởi vì bạn sẽ phải tính đến vụ nổ tổ hợp tương tác giữa preprocessconstructAttempt (và thậm chí có thể là getQueuedItems, mặc dù âm thanh tương đối đơn giản).

Thay vào đó, bạn nên viết kiểm tra cho các phương pháp cấp dưới - và các thử nghiệm cho constructAttempt cần tính đến tất cả các hiệu ứng tiềm năng của tiền xử lý. Nếu bạn không thể mô phỏng các tác dụng phụ đó (bằng cách điều khiển hàng đợi cơ bản hoặc kiểm tra kép) hãy refactor lớp của bạn cho đến khi bạn có thể.

1

Tôi nói để bộ thử nghiệm của bạn chỉ gọi go() vì đây là API công khai duy nhất. Điều đó có nghĩa là một khi bạn đã bao hàm tất cả các kịch bản cho phương thức go (sẽ bao gồm tiền xử lý và hàng đợi) thì nó không còn quan trọng nếu bạn thay đổi việc thực thi nội bộ. Lớp học của bạn vẫn chính xác như xa như sử dụng công cộng là có liên quan. Tôi hy vọng bạn đang sử dụng sự đảo ngược phụ thuộc cho các lớp được sử dụng cho tiền xử lý/hàng đợi - theo cách đó bạn có thể kiểm tra tiền xử lý độc lập và sau đó thử nó trong bài kiểm tra go() của bạn.

3

@Jeff có nó đúng. Điều bạn thực sự có là hai trách nhiệm xảy ra trong đối tượng này. Bạn có thể muốn kéo các mục được xếp hàng vào lớp của riêng mình.Nhấn preprocessconstructAttempt vào các mục riêng lẻ. IMHO khi bạn có một lớp học giao dịch với các mục riêng lẻ và một danh sách các mục bạn có mùi mã. Trách nhiệm là các thùng chứa danh sách hoạt động trên các mục.

public void go() { 
    for (QueuedItem item : getQueuedItems()) { 
     item.preprocess(); 
     item.constructAttempt(); 
    } 
} 

Lưu ý: Đây là tương tự như làm việc với một mô hình đối tượng lệnh

[EDIT 1a] Điều này làm cho nó thực sự dễ dàng để thử nghiệm với chế nhạo. Phương pháp go chỉ cần thử nghiệm với một mục hàng đơn lẻ hoặc không có mục hàng đợi. Ngoài ra, mỗi item giờ đây có thể có các bài kiểm tra cá nhân riêng biệt với vụ nổ tổ hợp của go.

[EDIT 1b] Bạn thậm chí có thể có thể gấp preprocess vào mục:

public void go() { 
    for (QueuedItem asyncCommunication: getQueuedItems()) { 
     asyncCommunication.attempt(); 
    } 
} 

Bây giờ bạn có một sự thật command pattern.

Các vấn đề liên quan