Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 一个列表上洗牌方法的单元测试_Java_Unit Testing_Junit - Fatal编程技术网

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();
    }
}