我正在为自己做一个小项目,我正在利用它作为一个了解单元测试和维护正确文档的机会。
我有一个Deck
课,代表一副牌(这很简单,说实话,我可以肯定它没有单元测试,但就像我说我已经习惯了使用单元测试)它有一个shuffle()
方法,可以改变卡片中卡片的顺序。
实施非常简单,肯定会有效:
public void shuffle()
{
Collections.shuffle(this.cards);
}
但是,我怎么能为这个方法实现单元测试。我的第一个想法是在调用shuffle()
之后检查牌组的顶牌是否有所不同,但当然有可能它是相同的。我的第二个想法是检查整个卡的顺序是否已经改变,但它们可能也是相同的顺序。那么,我怎么能编写一个测试来确保这种方法适用于所有情况呢?而且,一般来说,如何对结果取决于某些随机性的方法进行单元测试?
干杯,
皮特
答案 0 :(得分:5)
断言你的随机播放方法是否真的洗牌是非常困难的,如果不是不可能的话。默认随机数生成器只是随机到一定程度。测试你是否对这种随机性感到满意是不可能的,因为这需要花费太多时间。你实际测试的是随机数生成器本身没有多大意义。
然而,您可以测试的是此方法的invariants。
您当然可以创建一个测试,检查在n
次洗牌的序列中,没有返回重复的牌组。但偶尔这个测试可能会失败(但不太可能,正如其他答案中已经说过的那样)。
要考虑的其他因素是随机数生成器本身。如果这只是一个玩具项目,java.util.Random
就足够了。如果您打算创建一些在线纸牌游戏,请考虑使用java.security.SecureRandom
。
答案 1 :(得分:3)
首先,让我们考虑一下所涉及的可能性:
您不能保证随机播放不会按照确切的顺序放置卡片。但是,使用52张牌的概率是1/52! (即它很小,可能不值得担心。)
你肯定需要检查整个牌组,不过因为顶牌的概率与洗牌之前相同是1/52。
对于一般情况,并假设您正在使用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()正常工作。