如何以一定的随机性对函数进行单元测试

inv*_*sal 4 php phpunit unit-testing

我有返回 0 到 6 之间的三个随机元素数组的函数。它确保所有元素不能具有相同的值(您可以有两个具有相同值的元素,但不能是三个)。下面是一个示例代码。

public function getRandom() {
    $array = array(0, 0, 0);
    do {
        $array[0] = rand(0, 6);
        $array[1] = rand(0, 6);
        $array[2] = rand(0, 6);
    } while(($array[0] == $array[1]) && ($array[0] == $array[2]));
    return $array;
}
Run Code Online (Sandbox Code Playgroud)

我对单元测试有点陌生,唯一能想到的就是测试这个

  1. 测试此函数 1,000 次并检查它是否返回 0 到 6 之间的数据,并且所有三个元素不能具有相同的值。
  2. 找到一种方法来覆盖函数 rand() 以便它返回我们想要的:
    • 返回使所有三个元素具有相同值的值,然后返回具有相同值的两个元素的值。然后,返回具有不同值的所有元素的值。

我想知道是否有任何方法或我的哪种方法更适合这种情况。

Sve*_*ven 6

正如您猜对的那样,您无法真正可靠地测试随机性。

控制随机性的问题显示了您架构的弱点:您有一个要测试的类和方法,但在测试过程中无法控制正在调用的函数 ( rand())。

起初听起来可能很奇怪,但是如果你想用“受控”随机性进行测试,你需要以某种方式模拟随机函数,所以你需要一个围绕该 PHP 函数的包装器,它允许在测试期间拦截调用并返回定义的测试值时间。

想一想:如果您有一个具有随机性方法的类,并且该类的一个实例被注入到您的测试类中以提供随机值,您可以在测试期间模拟该对象并定义返回值. 任务完成。:)

现在,实例化具有单个方法的单个对象听起来很奇怪,该方法rand()将所有调用传递给同名的 PHP 函数。更奇怪的是需要在运行时将该对象传递给测试类以使其工作。但是您不必具有该运行时依赖项。您还可以重构您的测试类,以查看是否注入了随机性提供程序并使用它,如果没有,则rand()直接使用。

如果你想减少你的类的返工,有一个技巧来覆盖内置的 PHP 函数:如果你的类在一个命名空间内并调用一个本地 PHP 函数,那么该函数将首先在该命名空间内搜索。如果您rand()在与测试类相同的命名空间中声明一个在测试文件中命名的函数,该类将调用命名空间函数而不是 PHP 函数。然后,您只需要考虑如何预定义该模拟函数的返回值,但您可能可以使用全局变量或测试用例类的静态属性,该属性填充了预定义的“随机性”:

namespace MyNameSpace;
function rand() {
    return array_shift(RandomTest::$randomValues);
}

class RandomTest extends PHPUnit_Framework_TestCase {
    public static $randomValues = array();
    public function testSomeRandomness() {
        self::$randomValues = array(0,0,0,0,1,2);
        // ... test 
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我有那个工作,我会选择真正的 PHPUnit 模拟对象,一个将它注入到类中的 setter,并将其编码为默认调用rand().