【问题标题】:(homeworkish) unit test a class with random behavior without being able to mock the RNG(家庭作业)对具有随机行为的类进行单元测试,但无法模拟 RNG
【发布时间】:2014-06-26 13:49:14
【问题描述】:

我正在学习 Coursera 的算法,第一部分课程。我必须使用以下 API 创建一个 RandomizedQueue:

public class RandomizedQueue<Item> implements Iterable<Item> {
   public RandomizedQueue()                 // construct an empty randomized queue
   public boolean isEmpty()                 // is the queue empty?
   public int size()                        // return the number of items on the queue
   public void enqueue(Item item)           // add the item
   public Item dequeue()                    // delete and return a random item
   public Item sample()                     // return (but do not delete) a random item
   public Iterator<Item> iterator()         // return an independent iterator over items in random order
   public static void main(String[] args)   // unit testing
}

问题:如果我不能创建一个模拟 RNG 来传递到结构中(因为我不允许更改 API)并且我不想测试私有方法,如何我要测试这个结构的随机行为吗?

我的尝试

我尝试将我期望的结果视为一个概率问题。因此,例如,我将运行以下伪代码测试 10,000 次:

create new RandomizedQueue
enqueue 100 items (e.g. integers 0 - 99)
deque 1 item

然后我可以测试 100 项中的每一项的出队频率是否在某个置信区间内(基于二项分布)。

【问题讨论】:

    标签: unit-testing junit junit4


    【解决方案1】:

    有些人可能会称之为作弊,但我会beg to differ


    单元测试的重点是验证系统的行为。因此,如果一个测试完全有用,它必须是确定性的。否则你偶尔会得到假阴性,降低测试的完整性。 (“哦,如果测试失败也没关系。它有时会发生......”)

    考虑到这一点,如果你没有不能修改 API 的限制怎么办?如果你有你的 druthers,你将如何实现和测试你的类?首先创建那个实现。您可以将此类设计为完全确定的。

    在你有一个工作和测试类之后,只需在你的类中实现RandomizedQueue&lt;Item&gt;as an adapter


    示例

    考虑以下设置:

    public interface RandomNumberGenerator {
        int GetRandomInt();
    }
    
    // Identical to RandomizedQueue<T>, except takes a RandomNumberGenerator as a dependency
    public class MyRandomizedQueue<Item> implements Iterable<Item> {
    
        public MyRandomizedQueue(RandomNumberGenerator generator) {
        ...
    }
    

    您的测试可以为 SUT 提供一个虚假的 RandomNumberGenerator,并且可以完全控制任何方法的预期结果。

    在实际的 RandomizedQueue&lt;T&gt; 实现中,您将使用 real RandomNumberGenerator 实现(例如,使用 java.util.Random 的实现)实例化您的测试类,将其存储为成员变量,然后forward 方法调用它。喜欢:

    public Item dequeue() {
        return innerQueue.dequeue();
    }
    

    【讨论】:

    • 我认为下一步是分析性能以确定是否有必要进行重构以消除间接性,对吗?
    • 这不是一个坏主意。但是,如果将应用程序的性能瓶颈追溯到额外方法调用的开销,我会感到惊讶。 (优化之树通常有很多低垂的果实。):-)
    猜你喜欢
    • 1970-01-01
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-28
    • 1970-01-01
    • 2018-09-04
    • 2013-11-12
    相关资源
    最近更新 更多