Unit testing 我应该为任何可能的参数值编写单独的方法吗

Unit testing 我应该为任何可能的参数值编写单独的方法吗,unit-testing,phpunit,Unit Testing,Phpunit,我对单元测试概念非常陌生,我坚持写我的第一个概念 我有一个标准化ID值的方法。对于任何正数,它都应该返回传递值,即使它是一个包含数字的字符串,对于任何其他传递值,它都应该返回0 function normalizeId($val) { // if $val is good positive number return $val; // else return 0; } 我想为这个函数编写一个单元测试,并对任何可能的参数进行断言。例如: 5、-5、0、5、-5、3.14、fff、

我对单元测试概念非常陌生,我坚持写我的第一个概念

我有一个标准化ID值的方法。对于任何正数,它都应该返回传递值,即使它是一个包含数字的字符串,对于任何其他传递值,它都应该返回0

function normalizeId($val) {
    // if $val is good positive number return $val;
    // else return 0;
}
我想为这个函数编写一个单元测试,并对任何可能的参数进行断言。例如: 5、-5、0、5、-5、3.14、fff、新StdClass等

我应该在我的TestCase类中为这些条件中的任何一个编写一个方法,还是让一个方法在单独的行中包含所有条件? 即

最佳实践如何?我听说第二个选择很好,但我担心很多可能的参数值的很多方法。它可以是正数、负数、零、内有正数的字符串、内有负数的字符串、内有浮点数的字符串等

编辑

或者可能是第三种方法


我对PHP和您可以在其中使用的单元测试框架都不是很了解,但是在单元测试的一般领域中,出于这些原因,我推荐第二种方法

为特定类型的输入提供一个特定的测试用例fail,而不必遍历实际的Assert failure消息来找出哪个失败了

如果您决定需要使用多个输入对特定类型的转换执行测试,例如,如果您决定有一个包含1000个随机字符串的文本文件,并希望将这些字符串加载到测试驱动程序中,并通过以后的功能或验收测试

当您需要一些特殊的逻辑来设置时,可以更容易地更改各个测试用例

使您在错过转换类型时更容易发现,因为方法名称在对照检查表时更容易读取:

“可疑”可能会使您更容易发现您的god类可能需要内部重构的地方,使用单独的子类来执行特定类型的转换,而不是说您的方法是错误的,但您可能会发现一种转换类型的逻辑非常糟糕;当您回顾20或30个单独的测试用例时,这些用例可能会推动您咬紧牙关,开发更专业的转换器类


希望这能有所帮助。

我对PHP和您可以在其中使用的单元测试框架都不是很了解,但是在单元测试的一般领域,出于这些原因,我推荐第二种方法

为特定类型的输入提供一个特定的测试用例fail,而不必遍历实际的Assert failure消息来找出哪个失败了

如果您决定需要使用多个输入对特定类型的转换执行测试,例如,如果您决定有一个包含1000个随机字符串的文本文件,并希望将这些字符串加载到测试驱动程序中,并通过以后的功能或验收测试

当您需要一些特殊的逻辑来设置时,可以更容易地更改各个测试用例

使您在错过转换类型时更容易发现,因为方法名称在对照检查表时更容易读取:

“可疑”可能会使您更容易发现您的god类可能需要内部重构的地方,使用单独的子类来执行特定类型的转换,而不是说您的方法是错误的,但您可能会发现一种转换类型的逻辑非常糟糕;当您回顾20或30个单独的测试用例时,这些用例可能会推动您咬紧牙关,开发更专业的转换器类


希望这有帮助。

使用数据提供程序,因为您发现了自己。在多个方法中复制精确的测试用例,只改变参数和期望值是没有好处的

就我个人而言,我确实是从针对这种简单情况的“一体式测试”方法开始的。我会从一个简单的好案例开始,然后逐渐添加更多的案例。我可能不觉得有必要立即将其转变为数据提供者,因为它不会立即得到回报——但另一方面,情况可能会发生变化,而且这种测试结构可能是一种需要重构的短期解决方案


因此,每当您观察到自己在这样一个多测试用例方法中添加了更多的测试行时,请停止并使用数据提供程序来代替它。

使用数据提供程序,就像您自己发现的那样。在多个方法中复制精确的测试用例,只改变参数和期望值是没有好处的

就我个人而言,我确实是从针对这种简单情况的“一体式测试”方法开始的。我会从一个简单的好案例开始,然后逐渐添加更多的案例。我可能感觉不到压力 需要立即将其转换为数据提供程序,因为它不会立即得到回报——但另一方面,情况可能会发生变化,并且此测试结构可能是一个需要重构的短期解决方案


因此,当您观察到自己在这种多测试用例方法中添加了更多的测试行时,请停止并改用数据提供程序。

谢谢您的回答。我知道这有好处,但缺点呢?现在我必须写15个测试方法,每一个可能的输入值不确定,我认为所有这些只是一个小方法。也许使用提供者方法代替?我编辑了我的问题,以显示第三种使用提供者的方法。@djxak,不确定你所说的提供者方法是什么意思,但就缺点而言-是的,必须编写15+个测试方法有点痛苦,但我觉得它的优势超过了它-也许其他人能够提供第一种方法的一些观点…我编辑了我的问题,添加了第三种方法,但可能这种方法是针对PHPUnit的,在其他X单元库中不可用。@djxak啊,好,现在有意义了,是的,这也是一种很好且有效的方法,有点像我提到的,我们从一个文件中加载了一个值列表。对于方法3,我唯一的建议是添加一个参数来指示转换的数据类型-它将使任何断言失败消息更清楚地说明失败的原因。谢谢你的回答。我知道这有好处,但缺点呢?现在我必须写15个测试方法,每一个可能的输入值不确定,我认为所有这些只是一个小方法。也许使用提供者方法代替?我编辑了我的问题,以显示第三种使用提供者的方法。@djxak,不确定你所说的提供者方法是什么意思,但就缺点而言-是的,必须编写15+个测试方法有点痛苦,但我觉得它的优势超过了它-也许其他人能够提供第一种方法的一些观点…我编辑了我的问题,添加了第三种方法,但可能这种方法是针对PHPUnit的,在其他X单元库中不可用。@djxak啊,好,现在有意义了,是的,这也是一种很好且有效的方法,有点像我提到的,我们从一个文件中加载了一个值列表。对于方法3,我唯一的建议是添加一个参数来指示转换的数据类型-它将使任何断言失败消息更清楚地说明失败的原因。谢谢你的回答。很遗憾,我不能接受这两个答案,因为你确实补充了斯蒂芬的答案。谢谢你的回答。很遗憾,我不能接受这两个答案,因为你确实补充了斯蒂芬的答案。
public function testNormalizeId() {
    $this->assertEquals(5, MyClass::normalizeId(5));
    $this->assertEquals(0, MyClass::normalizeId(-5));
    $this->assertEquals(0, MyClass::normalizeId("fff"));
}
public function testNormalizeId_IfPositiveInt_GetPositiveInt() {
    $this->assertEquals(5, MyClass::normalizeId(5));
}
public function testNormalizeId_IfNegativeInt_GetZeroInt() {
   $this->assertEquals(0, MyClass::normalizeId(-5));
}
public function testNormalizeId_IfNotIntAsString_GetZeroInt() {
    $this->assertEquals(0, MyClass::normalizeId("fff"));
}
public function testNormalizeIdProvider()
{
    return array(
        array(5, 5),
        array(-5, 0),
        array(0, 0),
        array(3.14, 0),
        array(-3.14, 0),
        array("5", 5),
        array("-5", 0),
        array("0", 0),
        array("3.14", 0),
        array("-3.14", 0),
        array("fff", 0),
        array("-fff", 0),
        array(true, 0),
        array(array(), 0),
        array(new stdClass(), 0),
    );
}

/**
 * @dataProvider testNormalizeIdProvider
 */
public function testNormalizeId($provided, $expected)
{
    $this->assertEquals($expected, MyObject::normalizeId($provided));
}