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

ski*_*iwi 5 java junit unit-testing

考虑以下类:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我将如何对shuffle()方法进行单元测试?我正在使用 JUnit 4 进行测试。

我有以下选择:

  1. 测试shuffle()以查看它不会生成异常。
  2. 测试shuffle()并检查牌组是否真的被洗牌了。

选项 2 的示例伪代码:

while notShuffled
    create new Deck
    take cards and check if they are shuffled
Run Code Online (Sandbox Code Playgroud)

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

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

Lil*_*ste 5

目前,您的类与函数紧密耦合Collections.shuffle。静态函数因使测试变得更加困难而臭名昭著。(最重要的是,您测试没有意义Collections.shuffle;大概,它可以正常工作。)

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

public interface ICardShuffler {
    void shuffle(List<Card> cards);
}
Run Code Online (Sandbox Code Playgroud)

然后,您的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);
    }
    ...
Run Code Online (Sandbox Code Playgroud)

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

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

public class CollectionsCardShuffler implements ICardShuffler {
    public void shuffle(List<Card> cards) {
        Collections.shuffle(cards);
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:除了便于测试之外,此接缝还允许您实现新的 shuffle 方法,而无需修改Deck.