2010-05-30 48 views
10

Tôi đang làm việc trên một dự án nhỏ cho bản thân mình vào lúc này và tôi đang sử dụng nó như một cơ hội để làm quen với kiểm tra đơn vị và duy trì tài liệu thích hợp.Phương pháp kiểm tra JUnit với tính chất ngẫu nhiên

Tôi có một lớp Deck với một bộ bài (rất đơn giản và trung thực, tôi có thể chắc chắn rằng nó hoạt động mà không có bài kiểm tra đơn vị, nhưng như tôi đã nói) và nó có phương thức shuffle() thay đổi thứ tự của các thẻ trong boong.

Việc thực hiện rất đơn giản và chắc chắn sẽ làm việc:

public void shuffle() 
{ 
    Collections.shuffle(this.cards); 
} 

Nhưng, làm thế nào tôi có thể thực hiện một thử nghiệm đơn vị cho phương pháp này. Suy nghĩ đầu tiên của tôi là kiểm tra xem thẻ trên cùng của boong tàu có khác biệt không sau khi gọi shuffle() nhưng tất nhiên có khả năng là nó sẽ giống nhau. Suy nghĩ thứ hai của tôi là kiểm tra xem toàn bộ trật tự của thẻ có thay đổi hay không, nhưng một lần nữa chúng có thể có cùng thứ tự. Vì vậy, làm thế nào tôi có thể viết một bài kiểm tra đảm bảo phương pháp này hoạt động trong mọi trường hợp? Và, nói chung, làm thế nào bạn có thể đơn vị kiểm tra các phương pháp mà kết quả phụ thuộc vào một số ngẫu nhiên?

Chúc mừng,

Pete

+1

Từ góc độ hợp lý, thẻ của bạn có bị coi là xáo trộn nếu đơn đặt hàng vẫn giữ nguyên không? –

+1

Đối với câu hỏi cuối cùng của bạn, hãy xem http://stackoverflow.com/questions/122741/testing-for-random-value-thoughts-on-this-approach. Tôi cũng nghĩ rằng D. Knuth có cả một chương về nó. – ewernli

+1

Tôi nghĩ rằng nó có ý nghĩa cho sàn được coi là xáo trộn nếu các thẻ vẫn còn theo thứ tự và một số đặt hàng lại đã xảy ra, mặc dù khó có thể trộn một bộ bài thực sự trở lại theo thứ tự mà chúng ban đầu in – Peter

Trả lời

5

Khẳng định liệu shuffle của bạn phương pháp thực sự xáo trộn các thẻ là rất khó nếu không phải là không thể. Trình tạo số ngẫu nhiên mặc định chỉ ngẫu nhiên ở một mức độ nhất định. Không thể kiểm tra xem bạn có hài lòng với mức độ ngẫu nhiên này hay không vì nó sẽ mất quá nhiều thời gian. Những gì bạn đang thực sự thử nghiệm là máy phát điện số ngẫu nhiên mà không có ý nghĩa nhiều.

Tuy nhiên, những gì bạn có thể kiểm tra là invariants của phương pháp này.

  • Nếu bạn thoát phương thức, sẽ có cùng số lượng thẻ giống nhau trong boong như khi bạn nhập.
  • Phương thức trộn không được giới thiệu các bản sao.

Tất nhiên, bạn có thể tạo một kiểm tra để kiểm tra theo thứ tự n bộ trộn không có bản sao trùng lặp được trả về. Nhưng một lần trong một thời gian thử nghiệm này có thể thất bại (tuy nhiên không, như đã nêu trong các câu trả lời khác).

Điều gì đó khác để tính đến chính là trình tạo số ngẫu nhiên. Nếu đây chỉ là một dự án đồ chơi, thì java.util.Random là đủ. Nếu bạn có ý định tạo một số trò chơi bài trực tuyến, hãy cân nhắc sử dụng java.security.SecureRandom.

0

Tôi giả sử bạn có 52 thẻ trong boong của bạn. Khả năng nhận được cùng một thứ tự trong hai cuộc gọi tiếp theo là rất thấp, vì vậy tôi sẽ không bận tâm về nó quá nhiều. Nhưng, nếu bạn bắt đầu nhận được các bản tương tự nhiều lần, tôi nghĩ rằng nó an toàn để nói rằng bạn có một số vấn đề với máy phát điện số ngẫu nhiên của bạn.

Vì vậy, câu trả lời: kiểm tra xem thứ tự có khác với toàn bộ boong tàu không.

Ngoài ra, tôi nghĩ rằng bạn có thể an toàn yêu cầu phương thức shuffle() của bạn không trả lại thẻ theo cùng thứ tự hai lần liên tiếp. Và nếu bạn muốn hoàn toàn chắc chắn tuân theo yêu cầu đó, bạn có thể kiểm tra sự tương tự trong việc triển khai phương pháp.

+1

Ngoài ra kiểm tra xem bộ thẻ được đại diện bởi mỗi tầng là như nhau (ví dụ, cùng một số thẻ, cùng một thành viên khi đơn đặt hàng bị bỏ qua). –

3

Trước hết, chúng ta hãy suy nghĩ về các xác suất liên quan đến:

  1. Bạn không thể đảm bảo rằng các shuffle sẽ không đặt các thẻ trong thứ tự chính xác. Tuy nhiên, xác suất làm điều này với một boong 52 thẻ là 1/52! (nghĩa là nó tối thiểu và có thể không đáng lo ngại.)

  2. Bạn chắc chắn sẽ cần kiểm tra toàn bộ boong tàu, mặc dù xác suất của thẻ trên cùng giống như trước khi trộn là 1/52.

Đối với trường hợp chung và giả sử bạn đang sử dụng trình tạo số java.util.Random, chỉ cần khởi tạo nó bằng cùng một hạt giống. Sau đó, đầu ra cho đầu vào được xác định trước sau đó có thể lặp lại.

Tuy nhiên, đặc biệt cho các trường hợp này, giả sử bạn đã không được thực hiện riêng bạn List tôi không thực sự nhìn thấy điểm trong thử nghiệm Collections.shuffle(List<?> list) hoặc Collections.shuffle(List<?> list, Random rnd) (API link) vì đây là những chỉ là một phần của Java API.

0

Câu hỏi thú vị. Theo tôi, cách tốt nhất là để lưu trữ từng "shuffle" trong một bộ sưu tập, sau đó so sánh sau mỗi shuffle nếu deck của bạn phù hợp với bất kỳ "decks" trước đó trong bộ sưu tập.

Tùy thuộc vào số tiền của "Ngẫu nhiên" bạn yêu cầu bạn sẽ làm tăng số tiền của sàn xáo trộn bạn lưu trữ trong đó đơn vị kiểm tra tức là sau 50 shuffle bạn sẽ có một bộ sưu tập của 50 "sàn"

2

Cách tiếp cận khác sẽ là sử dụng phương pháp shuffle(List<?> list, Random random) và để tiêm một cá thể Random được gieo với một hằng số.

Bằng cách đó, kiểm tra JUnit của bạn có thể chạy một loạt các cuộc gọi và kiểm tra đầu ra là kết quả mong đợi.

Việc triển khai bình thường của lớp học của bạn sẽ tạo ra một cá thể Random không được nhận dạng.

1

Bạn đang thực sự ủy thác tất cả công việc khó khăn cho lớp học java.util.Collections. Đây là một lớp trung tâm trong API thu thập của Java và bạn chỉ nên giả định rằng nó hoạt động giống như bạn có thể làm với lớp java.lang.String.

Tôi muốn giới thiệu mã chống lại giao diện và giả lập/gỡ bỏ lớp triển khai của bạn bằng phương pháp shuffle(). Sau đó, bạn chỉ có thể khẳng định rằng các cuộc gọi của bạn trên phương thức shuffle() thực sự được gọi từ thử nghiệm của bạn thay vì kiểm tra chính xác giống như những người Sun/Oracle đã thử nghiệm kỹ lưỡng trước đây.

Điều này cho phép bạn tập trung hơn vào việc kiểm tra mã của riêng mình, nơi có 99,9% tất cả các lỗi có thể được đặt. Và nếu bạn thay thế phương thức java.util.Collections.shuffle() bằng một phương pháp từ một khung công tác khác hoặc triển khai của riêng bạn, kiểm tra tích hợp của bạn sẽ vẫn hoạt động!

Tôi hiểu rằng bạn đang làm điều này bởi vì bạn muốn tìm hiểu và tôi tin rằng kiến ​​thức về khoanh vùng/bỏ đi logic từ các khung công tác khác rất hữu ích như một phần kiến ​​thức kiểm tra của bạn.

0

Hầu hết mọi người dường như đều có ý kiến ​​rằng bạn nên kiểm tra những gì bạn đang thử nghiệm. Bằng cách đó tôi có nghĩa là những gì bạn đang xây dựng (hoặc tích hợp, khi bạn đang đảm bảo một thư viện của bên thứ ba thực sự làm những gì nó nói nó).

Nhưng bạn không nên tự kiểm tra ngôn ngữ Java.

Nên có một số nguyên tắc thử nghiệm như "Don't Test PlusEquals".

0

Tôi đã làm việc trên các số ngẫu nhiên trong khuôn khổ mô phỏng và mô phỏng và đứng trước một vấn đề tương tự: Làm cách nào tôi có thể thực hiện kiểm tra đơn vị PRNG của mình. Cuối cùng tôi thực sự không làm điều đó. Những gì tôi đã làm thay vào đó là để thực hiện một vài kiểm tra sanity. Ví dụ, các PRNG của chúng ta đều quảng cáo chúng tạo ra bao nhiêu bit, vì vậy tôi đã kiểm tra xem các bit đó có thực sự thay đổi hay không (và 10k lần lặp) và tất cả các bit khác là 0. Tôi đã kiểm tra hành vi thích hợp liên quan đến hạt giống. Tôi đã quyết định đưa các thử nghiệm ngẫu nhiên thực tế vào một giao diện người dùng tương tác để chúng có thể được kiểm tra bất cứ khi nào mong muốn nhưng đối với các thử nghiệm đơn vị, kết quả không xác định không phải là tốt đẹp, tôi nghĩ.

0

Bạn có thể phát ngẫu nhiên nhiều lần, theo dõi số lần Ace of spades (hoặc một số thẻ khác hoặc tất cả các thẻ khác) kết thúc bằng thẻ đầu tiên trong boong tàu. Về mặt lý thuyết, thẻ sẽ kết thúc ở trên khoảng 1 trong số 52 lần xáo trộn. Sau khi tất cả dữ liệu đã được thu thập, so sánh tần số thực tế với số 1/52 và kiểm tra xem sự khác biệt (giá trị tuyệt đối) có thấp hơn một số giá trị epsilon đã chọn hay không. Bạn càng xáo trộn, giá trị epsilon càng nhỏ. Nếu phương thức shuffle() của bạn đặt thẻ ở trên cùng bên trong ngưỡng epsilon của bạn, bạn có thể chắc chắn rằng nó là ngẫu nhiên các thẻ như bạn muốn.

Và bạn không phải dừng lại ở thẻ trên cùng. Bạn có thể kiểm tra xem mỗi vị trí trong boong có mang lại kết quả tương tự không. Làm điều đó với một thẻ, làm điều đó sẽ tất cả các thẻ, nó có thể không quan trọng. Nó có thể là quá mức cần thiết, nhưng nó sẽ đảm bảo shuffle() của bạn hoạt động chính xác.

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