4

我有返回 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;
}

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

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

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

4

2 回答 2

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

如果我有这份工作,我会选择真正的 PHPUnit 模拟对象,一个将其注入类的 setter,并将其编码为默认调用的可选依赖项rand()

于 2013-09-11T21:14:58.057 回答
1

第二种解决方案确实是最好的;当然,这需要为要存根的 rand 函数创建某种接口。

如果无法更改代码(即第三方代码),则第一个解决方案是唯一可能的解决方案。记住,单元测试应该很快,而大量的迭代对此无济于事。

作为第三个选项,您可以使用所谓的详尽测试:使用每个可能的值并进行单元测试来测试值的所有组合,即 [0, 0, 0], [0, 0, 1] ... 直到 [5, 5, 5]。这意味着 6^3 = 216 个单元测试,但您可以确定所有组合都经过测试并且比解决方案 1 更好。

于 2013-09-11T12:28:31.103 回答