具有随机性的JUnit测试方法

时间:2010-05-30 12:57:41

标签: java unit-testing junit random

我正在为自己做一个小项目,我正在利用它作为一个了解单元测试和维护正确文档的机会。

我有一个Deck课,代表一副牌(这很简单,说实话,我可以肯定它没有单元测试,但就像我说我已经习惯了使用单元测试)它有一个shuffle()方法,可以改变卡片中卡片的顺序。

实施非常简单,肯定会有效:

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

但是,我怎么能为这个方法实现单元测试。我的第一个想法是在调用shuffle()之后检查牌组的顶牌是否有所不同,但当然有可能它是相同的。我的第二个想法是检查整个卡的顺序是否已经改变,但它们可能也是相同的顺序。那么,我怎么能编写一个测试来确保这种方法适用于所有情况呢?而且,一般来说,如何对结果取决于某些随机性的方法进行单元测试?

干杯,

皮特

9 个答案:

答案 0 :(得分:5)

断言你的随机播放方法是否真的洗牌是非常困难的,如果不是不可能的话。默认随机数生成器只是随机到一定程度。测试你是否对这种随机性感到满意是不可能的,因为这需要花费太多时间。你实际测试的是随机数生成器本身没有多大意义。

然而,您可以测试的是此方法的invariants

  • 如果您退出该方法,则套牌中的卡片数量应与您输入时相同。
  • shuffle方法不应该引入重复项。

您当然可以创建一个测试,检查在n次洗牌的序列中,没有返回重复的牌组。但偶尔这个测试可能会失败(但不太可能,正如其他答案中已经说过的那样)。

要考虑的其他因素是随机数生成器本身。如果这只是一个玩具项目,java.util.Random就足够了。如果您打算创建一些在线纸牌游戏,请考虑使用java.security.SecureRandom

答案 1 :(得分:3)

首先,让我们考虑一下所涉及的可能性:

  1. 您不能保证随机播放不会按照确切的顺序放置卡片。但是,使用52张牌的概率是1/52! (即它很小,可能不值得担心。)

  2. 你肯定需要检查整个牌组,不过因为顶牌的概率与洗牌之前相同是1/52。

  3. 对于一般情况,并假设您正在使用java.util.Random数字生成器,只需使用相同的种子初始化它。然后,预先确定的输入的输出应该是可重复的。

    但是,特别是对于这种情况,假设您没有实现自己的List我真的没有看到测试Collections.shuffle(List<?> list)Collections.shuffle(List<?> list, Random rnd)API link )因为这些只是Java API的一部分。

答案 2 :(得分:2)

另一种方法是使用shuffle(List<?> list, Random random)方法并注入一个用常量播种的Random实例。

这样你的JUnit测试可以运行一系列调用并检查输出是否是预期的输出。

您的类的正常实现将创建一个未被种子的Random实例。

答案 3 :(得分:1)

您实际上已将所有辛勤工作委托给java.util.Collections班级。这是Java集合API中的一个中心类,您应该假设它的工作方式与java.lang.String类可能相同。

我宁愿建议使用shuffle()方法对接口进行编码并使用shuffle()方法模拟/存根实现类。然后,您可以断言您对java.util.Collections.shuffle()方法的调用实际上是从您的测试中调用的,而不是像Sun / Oracle人员之前完全测试过的那样完全相同。

这使您可以更专注于测试自己的代码,其中99.9%的错误可能位于其中。例如,如果您将{{1}}方法替换为另一个框架或您自己的实现,那么您的集成测试仍然有效!

我知道你这样做是因为你想要学习,我相信从其他框架中删除/剔除逻辑的知识作为你测试知识的一部分是非常有用的。

答案 4 :(得分:0)

我想你的牌组中有52张牌。在两个后续调用中获得相同顺序的可能性非常低,所以我不会太在意它。但是,如果你开始多次开始使用类似的套牌,我认为可以肯定地说你的随机数生成器存在一些问题。

所以,答案是:检查整个套牌的订单是不同的。

另外,我认为您可以安全地要求您​​的shuffle()方法不要连续两次以相同的顺序返回卡片。如果你想绝对确保遵循这个要求,你可以检查方法实现中的相似性。

答案 5 :(得分:0)

有趣的问题。在我看来,最好的方法是将每个“shuffle”存储在一个集合中,然后在每次shuffle之后进行比较,如果你的套牌与集合中任何先前的“套牌”相匹配。

根据“随机性”的大小,您需要增加存储在单元测试中的洗牌组的数量,即在50次洗牌之后,您将拥有50个“甲板”的集合

答案 6 :(得分:0)

大多数人似乎认为您应该测试您正在测试的内容。我的意思是你正在构建(或整合,当你确保第三方库实际上做它所说的那样)。

但是你不应该测试Java语言本身。

应该有一些测试原则,比如“不要测试PlusEquals”。

答案 7 :(得分:0)

我在建模和模拟框架中研究过随机数,并且遇到了类似的问题:我如何实际对PRNG实现进行单元测试。最后我实际上没有这样做。我做的是做一些健全检查。例如,我们的PRNG都宣告他们生成了多少比特,所以我检查这些比特是否确实发生了变化(大约10k次迭代),所有其他比特都是0.我检查了种子的正确行为(用相同的方法初始化PRNG)种子必须产生相同的数字序列,等等。然后我决定将实际的随机性测试放入交互式UI中,以便在需要时对它们进行测试,但对于单元测试,非确定性结果并不是那么好,我想。

答案 8 :(得分:0)

你可以反复洗牌,跟踪黑桃王牌(或其他牌,或所有其他牌)最终成为牌组中第一张牌的次数。从理论上讲,这张牌应该在52次洗牌中占据最重要的位置。收集完所有数据后,将实际频率与数字1/52进行比较,并检查差值(绝对值)是否低于某个选定的epsilon值。您洗牌的次数越多,您的epsilon值就越小。如果你的shuffle()方法将卡放在你的epsilon阈值的顶部,你可以确定它是按照你想要的方式随机化卡片。

而且你不必只停留在顶级卡片上。您可以测试卡组中的每个位置是否给出相同的结果。用一张卡做,所有卡都做,这可能没关系。它可能有点矫枉过正,但它可以保证你的shuffle()正常工作。