Java 一个列表上洗牌方法的单元测试
考虑以下类别:Java 一个列表上洗牌方法的单元测试,java,unit-testing,junit,Java,Unit Testing,Junit,考虑以下类别: 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 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();
}
}
这里唯一的罪魁祸首是,当执行为选项2编写的测试(它也继承了选项1)时,如果洗牌没有按预期工作,那么代码执行将永远不会停止
我将如何解决这个问题?是否可能限制JUnit测试中的执行时间?我将执行以下操作:
- 首先,我将使
类实现Deck
李>Iterator
- 第二,在第一次调用
或shuffle
之后,我会使take
类不可变。即不允许添加卡Deck
static List<List<Card>> previousDecks = new ArrayList<>();
static List<Card> inputList = createListOfCards();
static{
privousDecks.add(inputList);
}
@Test
public void testShuffle(){
Deck deck = new Deck(inputList);
deck.shuffle();
List<Card> shuffled = new ArrayList<>();
Iterators.addAll(shuffled, deck);
assertThat(shuffled,
IsIterableContainingInAnyOrder.containsInAnyOrder(inputList));
for (List<Card> previouslySeen : previousDecks){
assertThat(shuffled,
CoreMatchers.not(
IsIterableContainingInOrder.contains(previouslySeen )));
}
previousDecks.add(shuffled);
}
静态列表previousDecks=newarraylist();
静态列表inputList=createListOfCards();
静止的{
添加(输入列表);
}
@试验
公共void testShuffle(){
甲板=新甲板(输入列表);
洗牌();
List shuffled=new ArrayList();
迭代器.addAll(洗牌,套牌);
断言(洗牌),
ISiterableContainingAnyOrder.containsInAnyOrder(inputList));
for(列表previouslySeen:previousDecks){
断言(洗牌),
CoreMatchers.not(
ISiterableContainingOrder.contains(先前可见));
}
上一组。添加(混洗);
}
然后我会找到一种方法,多次运行testShuffle
,以确保每次洗牌不会产生相同的结果。这可以通过多种方式实现。这就是一个例子
仅供参考,我这里用的是火腿肠和番石榴
我不明白你的伪代码。。。为什么要使用while循环?只需在牌组上调用shuffle。如果抛出异常,测试将失败。如果甲板顺序相同,则测试失败。您还需要更多吗?目前,您的类使用的是
Collections.shuffle
函数。静态函数因使测试更加困难而臭名昭著。(除此之外,测试Collections.shuffle
,毫无意义;据推测,它工作正常。)
为了解决这个问题,您可以在类中为这个洗牌功能引入一个。这是通过将shuffle
函数提取到一个角色(由接口表示)来完成的。例如:
public interface ICardShuffler {
void shuffle(List<Card> cards);
}
这允许您的单元测试使用模拟对象来验证预期行为是否发生(即,shuffle
在提供的ICardShuffler
上调用shuffle
)
最后,您可以将当前功能移动到此接口的实现中:
public class CollectionsCardShuffler implements ICardShuffler {
public void shuffle(List<Card> cards) {
Collections.shuffle(cards);
}
}
公共类CollectionsCardShuffler实现ICardShuffler{
公共无效洗牌(列表卡){
收集。洗牌(卡片);
}
}
注意:除了方便测试外,此seam还允许您实现新的洗牌方法,而无需修改
Deck
中的任何代码。您还可以编写自己的类CollectionHelper并使用它来代替集合
public class CollectionsHelper {
private CollectionsHelper(){}
private static boolean isTest = false;
private static int n = 0;
public static void shuffle(List<?> l){
if(!isTest) Collections.shuffle(l);
else Collections.shuffle(l, new Random(n));
}
public static void setTest(){
isTest = true;
n = 0;
}
public static boolean isTest(){
return isTest;
}
public static void setSeedForTest(int seed){
n = seed;
}
}
“那么代码执行将永远不会停止。”我认为您没有考虑的另一个问题是,如果shuffle方法保持列表的顺序不变。我想这取决于你对洗牌法的期望。至于一个永远无法完成的方法,理论上,这是任何正在测试的方法的问题。我不确定您是否需要明确担心这一点。@fge现在为此添加了伪代码,但没有意识到它不清楚。@StealthRabbi(在编辑中澄清了它)我认为我的
Deck.shuffle
方法保持列表不变,然而,我并不认为收集。洗牌的实现可能会被破坏。@Vakh你的回答是完全正确的,我想取消删除以接收我的向上投票?while循环用于通过调用take
来检索卡牌洗牌是否意味着必须更改顺序?如果顺序没有更改,然后是一个糟糕的洗牌,但好吧。。。你赢了语义之战。@JohnB我认为你需要更多的伪代码,因为它仍然不清楚。你是在暗示你正在创造多个甲板。很好的回答WRT设计。然而,我不认为在这里仅仅验证mock是在shuffle
方法中调用的就足够了。如果传递给shuffler的列表与在take
中迭代的列表不同,则这两个方法之间的正确联系将不成立。我认为更好的解决方案是在shuffle
中使用默认行为,并测试shuffle对take
的影响。这是基于这样的想法,shuffle
的唯一要求是更改take
返回的卡的顺序。您无法通过单独测试shuffle
来测试此联系人。@JohnB“如果传递给shuffler的列表与在take
中迭代的列表不同,shuffle
的单元测试应验证是否将正确的列表传递给ICardShuffler.shuffle
方法。(事实上,这是测试需要验证的唯一内容。)如果单元测试失败,那么这两个方法之间的契约无效(因为根据定义,shuffle
将被视为破坏)。不同意。似乎您想要测试shuffle
的实现,而不是这两种方法之间的契约:suffle
&take
。例如,如果我想重新编译代码,以维护新游戏的原始卡片列表,我可以将不同的列表传递给洗牌器。实际上,我明确尝试不测试集合的实现。洗牌:-)在任何情况下,如果您将不同的列表传递给ICardShuffler.shuffle
,它会破坏take
的预期行为,就像您将不同的列表传递给Collections.shuffle一样。如果
public class CollectionsCardShuffler implements ICardShuffler {
public void shuffle(List<Card> cards) {
Collections.shuffle(cards);
}
}
public class CollectionsHelper {
private CollectionsHelper(){}
private static boolean isTest = false;
private static int n = 0;
public static void shuffle(List<?> l){
if(!isTest) Collections.shuffle(l);
else Collections.shuffle(l, new Random(n));
}
public static void setTest(){
isTest = true;
n = 0;
}
public static boolean isTest(){
return isTest;
}
public static void setSeedForTest(int seed){
n = seed;
}
}
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() {
CollectionsHelper.shuffle((List<Card>)queue);
}
public Card take() {
return queue.remove();
}
}