列表上洗牌方法的java单元测试
考虑以下类:
public class Deck {
private final Queue<Card> queue = new LinkedList<>();
public Deck() { }
public Deck(final Collection<Card> cards) {
Objects.requireNonNull(cards);
queue.addAll(cards);
}
public void add(final Card card) {
Objects.requireNonNull(card);
queue.add(card);
}
public void addAll(final Collection<Card> cards) {
Objects.requireNonNull(cards);
queue.addAll(cards);
}
public void shuffle() {
Collections.shuffle((List<Card>)queue);
}
public Card take() {
return queue.remove();
}
}
我如何对shuffle()
方法进行单元测试?我正在使用JUnit4进行测试
我有以下选择:
- 测试
shuffle()
以查看它是否不会生成异常李> - 测试
shuffle()
并检查牌组是否确实被洗牌李>
选项2的伪代码示例:
while notShuffled
create new Deck
take cards and check if they are shuffled
这里唯一的罪魁祸首是,当执行为选项2编写的测试(它也继承了选项1)时,如果洗牌没有按预期工作,那么代码执行将永远不会停止
我将如何解决这个问题?是否可能限制JUnit测试中的执行时间
# 1 楼答案
目前,您的类是tightly coupled,带有
Collections.shuffle
函数。静态函数因使测试更加困难而臭名昭著。(除此之外,测试Collections.shuffle
没有任何意义;据推测,它工作正常。)为了解决这个问题,您可以在类中引入一个seam来实现这个洗牌功能。这是通过将
shuffle
函数提取到一个角色(由接口表示)来实现的。例如:然后,可以将
Deck
类配置为保留对该接口的某个实现的实例的引用,并在必要时调用它:这允许您的单元测试使用test double,比如模拟对象,来验证预期的行为是否发生(即
shuffle
在提供的ICardShuffler
上调用shuffle
)最后,您可以将当前功能转移到该接口的实现中:
注意:除了方便测试之外,这个seam还允许您实现新的洗牌方法,而无需修改
Deck
中的任何代码# 2 楼答案
我不明白你的伪代码。。。为什么要使用while循环?只需在甲板上呼叫shuffle。如果抛出异常,测试将失败。如果甲板顺序相同,则测试失败。你还需要更多吗
# 3 楼答案
我会这样做:
Deck
类实现Iterator<Card>
李>shuffle
或take
之后,我将使Deck
类不可变。不允许添加卡片李>然后我会这样做
然后,我会找到一种方法,多次运行
testShuffle
,以确保每次洗牌不会产生相同的结果。这可以通过多种方式实现Here就是一个例子仅供参考,我这里用的是火腿肠和番石榴
Iterators
Hamcrest
# 4 楼答案
您还可以编写自己的类CollectionHelper,并使用它来代替集合
在每个测试开始时,您可以调用
CollectionsHelper.setTest()
来使用确定性洗牌。 你的班级看起来像: