在列表上对shuffle方法进行单元测试

时间:2014-04-16 12:22:33

标签: 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 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()方法进行单元测试?我正在使用JUnit 4进行测试。

我有以下选择:

  1. 测试shuffle()以确定它不会产生异常。
  2. 测试shuffle()并检查牌组是否实际上被洗牌。
  3. 选项2的伪代码示例:

    while notShuffled
        create new Deck
        take cards and check if they are shuffled
    

    这里唯一的罪魁祸首是,当执行为选项2编写的测试(也继承包含选项1)时,如果改组不能按预期工作,那么代码执行将永远不会停止。

    我该如何解决这个问题?是否可能限制JUnit测试中的执行时间?

4 个答案:

答案 0 :(得分:3)

目前,您的班级为tightly coupled且具有Collections.shuffle功能。静态函数因使事情更难以测试而臭名昭着。 (最重要的是,你测试Collections.shuffle没有意义;大概是正确的。{/ p>

为了解决这个问题,您可以在班级中引入seam来实现此改组功能。这是通过将shuffle函数提取到角色(由接口表示)来完成的。例如:

public interface ICardShuffler {
    void shuffle(List<Card> cards);
}

然后,您的Deck类可以配置为保持对此接口的某些实现的实例的引用,并在必要时调用它:

public class Deck {
    private final Queue<Card> queue = new LinkedList<>();

    private ICardShuffler cardShuffler;

    public Deck(ICardShuffler cardShuffler) {
        this.cardShuffler = cardShuffler;
    }
    ...
    public void shuffle() {
        cardShuffler.shuffle((List<Card>)queue);
    }
    ...

这允许您的单元测试使用test double(如模拟对象)来验证预期的行为是否发生(即,shuffle在提供的{{1}上调用shuffle }})。

最后,您可以将当前功能移动到此接口的实现中:

ICardShuffler

注意:除了便于测试之外,此接缝还允许您实现新的混洗方法,而无需修改public class CollectionsCardShuffler implements ICardShuffler { public void shuffle(List<Card> cards) { Collections.shuffle(cards); } } 中的任何代码。

答案 1 :(得分:1)

我不明白你的伪代码...为什么要使用while循环?只需在甲板上打电话。如果抛出异常,则测试失败。如果卡组的顺序相同,则测试失败。你还需要更多吗?

答案 2 :(得分:1)

您也可以编写自己的类 CollectionsHelper 并使用它代替 Collections。

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

在每次测试开始时,您可以调用 CollectionsHelper.setTest() 以使用确定性随机播放。 你的类看起来像:

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

答案 3 :(得分:0)

我会做以下事情:

  • 首先,我会制作Deck类工具Iterator<Card>
  • 其次,在第一次调用Deckshuffle后,我会使take类不可变。即。不允许添加卡片。

然后我会做这样的事情......

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

然后我会找到一种多次运行testShuffle的方法,以确保每次shuffle不会产生相同的结果。这可以通过多种方式完成。 Here就是一个例子。

作为一个FYI,我在这里使用Hamcrest和Guava。

Iterators

Hamcrest