使用返回随机结果的函数进行单元测试

Mic*_*tum 69 .net c# unit-testing xunit.net

我不认为这是特定于语言或框架,但我使用xUnit.net和C#.

我有一个函数返回一定范围内的随机日期.我传递了一个日期,返回日期总是在给定日期之前1到40年的范围内.

现在我只是想知道是否有一种很好的方法来进行单元测试.最好的方法似乎是创建一个循环并让函数运行100次并断言这100个结果中的每一个都在所需的范围内,这是我目前的方法.

我也意识到,除非我能够控制我的Random发生器,否则将没有一个完美的解决方案(毕竟,结果是随机的),但我想知道当你必须测试返回随机结果的功能时你采取了什么方法一定的范围?

Bri*_*sio 54

模拟或伪造随机数发生器

做这样的事情......我没有编译它,所以可能会有一些语法错误.

public interface IRandomGenerator
{
    double Generate(double max);
}

public class SomethingThatUsesRandom
{
    private readonly IRandomGenerator _generator;

    private class DefaultRandom : IRandomGenerator
    {
        public double Generate(double max)
        {
            return (new Random()).Next(max);
        }
    }

    public SomethingThatUsesRandom(IRandomGenerator generator)
    {
        _generator = generator;
    }

    public SomethingThatUsesRandom() : this(new DefaultRandom())
    {}

    public double MethodThatUsesRandom()
    {
        return _generator.Generate(40.0);
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的测试中,只需假冒或模拟IRandomGenerator即可返回罐装内容.

  • 顺便说一句,许多语言都有模拟框架,您可以使用它们来使模拟变得更简单。更强大的(例如,PowerMock)甚至可以允许覆盖对 RNG 的调用,而不需要依赖注入。 (2认同)

Squ*_*Cog 33

除了测试函数返回所需范围内的日期之外,还需要确保结果分布均匀.您描述的测试将传递一个函数,该函数只返回您发送的日期!

因此,除了多次调用函数并测试结果保持在所需范围内外,我还会尝试评估分布,可能是将结果放入存储桶并检查存储桶之后的数据量大致相等.完成.您可能需要超过100次调用才能获得稳定的结果,但这听起来并不像一个昂贵的(运行时明智的)函数,因此您可以轻松运行几次K迭代.

我之前遇到过非均匀"随机"功能的问题......它们可能是一个真正的痛苦,它值得早期测试.

  • 不同意这个答案 - 您正在有效地测试随机数生成器,也就是“其他人的代码”。如另一个答案中所述,伪造生成器是正确的方法。您的测试应该只检查随机生成器是否被调用以及它的结果是否按预期处理。 (2认同)

Rob*_*ney 9

我认为你测试这个问题有三个不同的方面.

第一个:我的算法是正确的吗?也就是说,如果一个功能正常的随机数生成器,它会产生在整个范围内随机分布的日期吗?

第二个:算法是否正确处理边缘情况?也就是说,当随机数发生器产生最高或最低允许值时,是否有任何破坏?

第三个:我的算法实现是否有效?也就是说,给定一个已知的伪随机输入列表,它是否产生了预期的伪随机日期列表?

前两个东西不是我构建到单元测试套件中的东西.它们是我在设计系统时证明的东西.我可能会写生成数不胜数的日期,并进行了卡方检验,作为daniel.rikowski建议测试工具做到这一点.我也想确保这个测试工具并没有终止,直到它处理两者的边缘的情况下(假设我的随机数的范围非常小,我可以不用它).我会记录这一点,以便任何人出现并尝试改进算法都会知道这是一个重大改变.

最后一个我要进行单元测试的东西.我需要知道,没有任何内容侵入到破坏其算法实现的代码中.当发生这种情况时我会得到的第一个标志是测试失败.然后我会回到代码中,发现其他人认为他们正在修理某些东西而不是破坏它.如果有人确实修复了算法,他们也会修复这个测试.


Ned*_*der 8

您无需控制系统即可使结果具有确定性.你采用了正确的方法:确定功能输出的重要性并测试.在这种情况下,重要的是结果在40天的范围内,并且您正在测试它.同样重要的是它并不总是返回相同的结果,所以也要测试它.如果你想成为更好的,你可以测试结果通过某种随机性测试..


flo*_*olo 5

Normaly我完全使用您建议的方法:控制随机生成器.使用默认种子初始化它以进行测试(或者通过代理返回适合我的测试用例的数字替换它),因此我具有确定性/可测试行为.