2010-05-30 25 views
10

目前我正在爲自己開發一個小型項目,並將其作爲熟悉單元測試和維護適當文檔的機會。JUnit具有隨機性質的測試方法

我有一個Deck類與代表一副撲克牌(這很簡單,說實話,我可以肯定它沒有單元測試,但像我說我習慣於使用單元測試),它有一個shuffle()方法可以改變牌組中牌的順序。

的實現是很簡單的,肯定會工作:

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

但是,我怎麼能實現此方法的單元測試。我的第一個想法是在打電話shuffle()後檢查牌組的頂牌是否不同,但當然有可能相同。我的第二個想法是檢查卡片的整個訂單是否發生了變化,但是他們又可能是按照相同的順序。那麼,我怎麼能寫一個測試來確保這種方法在所有情況下都能正常工作?而且,總的來說,你如何將單元測試方法的結果依賴於某種隨機性?

乾杯,

皮特

+1

從邏輯角度來看,如果訂單保持不變,您的卡片會被視爲洗牌嗎? – 2010-05-30 13:04:39

+1

對於你的最後一個問題,請參閱http://stackoverflow.com/questions/122741/testing-for-random-value-thoughts-on-this-approach。我也認爲D. Knuth有一整章的內容。 – ewernli 2010-05-30 17:15:47

+1

我認爲如果卡片保持相同的順序並且發生了一些重新排序,那麼將卡組視爲洗牌是合理的,儘管不太可能將一副真正的卡片洗回到它們的相同順序原來英寸 – Peter 2010-05-30 19:39:06

回答

5

集合斷言是否你的洗牌ammount的方法實際上洗牌是非常困難的,如果不是不可能的話。默認的隨機數發生器只在一定程度上是隨機的。不可能測試你對這種隨機性的滿意程度,因爲這會花費太多時間。你實際測試的是隨機數發生器本身,這沒有多大意義。

但是,您可以測試的是這種方法的invariants

  • 如果您退出該方法,卡組中的卡片數量應與您輸入的卡片數量完全相同。
  • 洗牌方法不應該引入重複。

你當然可以創建一個測試,檢查在一系列n洗牌中沒有重複的套牌。但偶爾這個測試可能會失敗(不過不太可能,正如其他答案中所述)。

其他要考慮的是隨機數生成器本身。如果這只是一個玩具項目,java.util.Random就足夠了。如果您打算創建一些在線紙牌遊戲,請考慮使用java.security.SecureRandom

0

我想你有52卡在甲板上。在接下來的兩次電話會議中獲得同樣的訂單的可能性非常低,所以我不會爲此煩惱太多。但是,如果您確實開始多次獲得類似的套牌,我認爲可以肯定地說您的隨機數字發生器存在一些問題。

所以,答案是:檢查整個卡組的訂單是不同的。

另外,我認爲你可以安全地讓你的shuffle()方法不要以連續兩次相同的順序返回卡片。如果您想確保遵循該要求,則可以檢查方法實現中的相似性。

+1

還要檢查每個牌組所代表的牌組是否相同(即相同數量的牌,一旦訂單被忽略,相同的成員)。 – 2010-05-30 13:06:36

3

首先,讓我們想想涉及到的概率:

  1. 不能保證洗牌不會將卡按正確的順序。但是,使用52張牌組的概率是1/52! (即它是最小的,可能不值得擔心)。

  2. 你一定會需要檢查整個卡組,雖然因爲頂牌與洗牌前相同的概率是1/52。

對於一般的情況下,假設你正在使用的java.util.Random數生成器,只要使用相同的種子初始化它。然後,預定義輸入的輸出應該是可重複的。

然而,專門針對這種情況,假設你還沒有實現自己的List我實在不明白在測試Collections.shuffle(List<?> list)Collections.shuffle(List<?> list, Random rnd)API link)點,因爲這些都只是在Java API的一部分。

0

有趣的問題。在我看來,最好的方法是將每個「洗牌」存儲在一個集合中,然後在每個洗牌之後比較,如果你的套牌與集合中的任何之前的「套牌」相匹配。

根據的「隨機性」你需要你會增加洗牌甲板您在單元測試即存儲ammount的50次後洗牌,你將有50「甲板」

2

另一種方法是使用shuffle(List<?> list, Random random)方法並注入一個帶常量的Random實例。

通過這種方式,您的JUnit測試可以運行一系列調用並檢查輸出是否爲預期輸出。

您的課程的正常實施將創建一個Random未註冊的實例。

1

你實際上將所有的辛苦工作交給了java.util.Collections班。這是Java集合API中的一箇中心類,您應該假設它的工作方式與您可能使用java.lang.String類相似。

我寧願推薦對接口進行編碼,並用shuffle()方法模擬/留存您的實現類。然後,您可以斷言您在shuffle()方法上的調用實際上是從您的測試中調用的,而不是像Sun/Oracle人員之前測試過的完全一樣。

這使您可以更專注於測試自己的代碼,其中99.9%的錯誤可能位於此處。例如,如果您將java.util.Collections.shuffle()方法替換爲另一個框架或您自己的實現方法,那麼您的集成測試仍然可行!

我明白你這樣做是因爲你想學習,而且我相信關於從其他框架中剔除/嘲弄邏輯的知識作爲測試知識的一部分非常有用。

0

大多數人似乎認爲你應該測試你正在測試的東西。我的意思是說你正在構建(或者整合,當你確定第三方庫實際上做了它所說的事情時)。

但是你不應該測試Java語言本身。

應該有一些測試原理,如「不要測試PlusEquals」。

0

我在建模和仿真框架隨機數的工作和以前類似的問題站在:我怎樣才能真正單元測試我們的PRNG實現。最後我其實並沒有這樣做。我所做的只是進行一些理智檢查。例如,我們的PRNG都宣告它們產生了多少位,所以我檢查了這些位是否真的發生了變化(大約10k次迭代),而其他所有位都是0.我檢查了種子的正確行爲(使用相同的PRNG初始化種子必須產生相同的數字序列)等。然後,我決定將實際的隨機性測試放入交互式UI中,以便在需要時進行測試,但對於單元測試,非確定性結果並不好,我想。

0

你可以多次洗牌,跟蹤的是在甲板上的頭牌黑桃A(或其他一些卡,或所有其他卡)多少次結束。從理論上來說,卡應該在52個洗牌中最多佔1個。在收集完所有數據後,將實際頻率與1/52比較,並檢查差異(絕對值)是否低於某個選定的ε值。你洗牌的越多,你的epsilon值就越小。如果您的shuffle()方法將卡片放置在您的epsilon閾值內,您可以確定它是隨機卡片。

而且你不必停止只是在頂級顯卡。您可以測試套牌中的每個位置是否給出相同的結果。做一張卡片,做所有卡片,這可能沒有關係。這可能是矯枉過正,但它會保證你的shuffle()工作正常。