我有一个函数,它返回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;
}
我对单元测试有点新意,唯一认为我可以考虑测试这个的是
我想知道是否有任何方法或我的方法对于这种情况更好。
答案 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.