如何以一些随机性对单元测试功能进行单元化

时间:2013-09-11 12:24:04

标签: php unit-testing phpunit

我有一个函数,它返回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. 测试此函数1,000次并检查它是否返回0到6之间的数据,并且所有三个元素都不能具有相同的值。
  2. 找到一种覆盖函数rand()的方法,以便它返回我们想要的内容:
    • 使所有三个元素具有相同值的返回值,然后返回具有两个具有相同值的元素的值。然后,返回具有不同值的所有元素的值。
  3. 我想知道是否有任何方法或我的方法对于这种情况更好。

2 个答案:

答案 0 :(得分:3)

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

控制随机性的问题表明您的体系结构存在缺陷:您有一个要测试的类和方法,但在测试期间无法控制被调用的函数(rand())。 / p>

一开始可能听起来很奇怪,但是如果你想用“受控”随机性进行测试,你需要以某种方式模拟随机函数,所以你需要一个围绕该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()的可选依赖项。

答案 1 :(得分:1)

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

如果代码无法更改(即第三方代码),则第一种解决方案是唯一可行的解​​决方案。请记住,单元测试应该很快,并且许多迭代对此没有帮助。

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