使用单个方法或每个setter验证输入

时间:2013-12-04 14:01:59

标签: php unit-testing phpunit logic

我刚刚开始使用测试驱动开发,我想知道在验证使用用户输入设置的属性时哪种做法最好。我不确定哪种方法更受欢迎,甚至最好。

以下是两种示例方法。

如果那里有一位古茹可以对这个问题有所了解,我将非常感激!

方法:一种验证方法

请考虑以下内容:我有一个带有getter和setter属性的类。第一种方法是使用单一方法验证所有集合属性。

/**
 * @returns bool TRUE if validates, FALSE if not
 */
public function validate()
{
// validate all set properties here
}

测试明智我需要考虑每个需要验证的属性的每个可能选项,在我的单元测试中产生类似的结果:

$this->oObject->setActionCodeGroupID(12);
$this->oObject->setFilename('filename.txt');
$this->oObject->setAmount(10000);
$this->oObject->setMaxUsage(10);
$this->oObject->setMinLength(9);
$this->oObject->setMaxLength(12);
$this->oObject->setPrefix('TM_');

$this->assertTrue($this->oObject->validate());

$this->oObject->setActionCodeGroupID(0);
$this->assertFalse($this->oObject->validate());
$this->oObject->setActionCodeGroupID(12);

$this->oObject->setFilename('');
$this->assertFalse($this->oObject->validate());
$this->oObject->setFilename('filename.txt');

$this->oObject->setAmount(0);
$this->assertFalse($this->oObject->validate());
$this->oObject->setAmount(10000);

$this->oObject->setMaxUsage(0);
$this->assertFalse($this->oObject->validate());
$this->oObject->setMaxUsage(10);

...

方法2:验证setter中的输入

此方法基本上执行标题所说的内容,它(如果需要)可以立即验证输入。如果输入没有验证,则会抛出异常。

public function setCodeGroup(CodeGroup $oCodeGroup)
{
    if (!($oCodeGroup instanceof CodeGroup)) {
        throw new \InvalidArgumentException('Expected instance of CodeGroup, got '. get_class($oCodeGroup));
    }
    $this->oCodeGroup = $oCodeGroup;
}

2 个答案:

答案 0 :(得分:1)

IMO最好的方法是方法1。 你能做的最好的就是测试“好”的情况,然后在设置了无效值时检查每个属性是否产生验证错误。

这是一种常见的xUnit模式。

一种实现这一目标的方法是拥有一个生成有效对象的方法,并使用它来测试所有情况。 对于无效的情况,您可以使用将传递setter方法名称和值的dataProvider。对于每种情况,您都要设置该值并检查该对象是否已无效。

基本上它与你正在做的一样,但是以更有条理的方式:

private function getValidFixture()
{
    $fixture = $this->oObject();
    $this->oObject->setActionCodeGroupID(12);
    $this->oObject->setFilename('filename.txt');
    $this->oObject->setAmount(10000);
    $this->oObject->setMaxUsage(10);
    $this->oObject->setMinLength(9);
    $this->oObject->setMaxLength(12);
    $this->oObject->setPrefix('TM_');
    return $this->oObject;
}

public function testValidObject()
{
    $this->assertTrue($this->getValidFixture()->validate());
}

public function invalidProperties()
{
    return array(
         array(
             $setter = "setActionCodeGroupID",
             $value = 0
         ),
         array(
             $setter = "setFilename",
             $value = ""
         ),

         /** more setters ... */

    );
}

/**
 * @dataProvider invalidProperties
 */
public function testInvalidObject($setter, $value)
{
     $fixture = $this->getValidFixture();
     $fixture->$setter($value);
     $this->assertFalse($fixture->validate());
}

答案 1 :(得分:1)

方法2不能因为你抛出异常而失败,因为它会因为违反类型提示而导致PHP方面失败。如果没有PHP抱怨,你不能将CodeGroup的非实例传递给这个函数。请注意,您可以将此参数设置为可选,然后允许显式传递默认值,该值必须不符合类型提示。这样你就可以允许NULL作为一个值,然后负担任务,总是检查你是否有一个正确类型的对象,“否则做其他事情。”

然而,使用类型提示是一个非常好的主意。它们将阻止您传递错误的对象,并使大多数IDE能够为您提供自动完成支持。

现在,我在验证方法中遇到的问题是,您将大量参数集中在一起,现在您需要正确检查大量的测试用例。在最坏的情况下,每个参数都有一个值范围有效,而上下两个范围无效。因此,对于单个参数,您至少有三个测试用例,可能是四个(最小和最大有效值,以及旁边的无效值)。

使用相同数量的单个测试添加第二个参数会增加测试用例的数量。现在,如果无法独立检查参数,则需要9或16个测试用例。

如果您可以独立检查每个参数验证,则需要三个或四个测试用例(两个参数的时间为2),另外一个组合测试用例,一个有效,一个无效参数,均为4其他情况,以测试有效和无效参数的组合是否正确组合。这样就可以得到3 + 3 + 4 = 10或4 + 4 + 4 = 12个测试用例,而不是9或16个。

输入第三个参数。如果没有单独的测试,你现在有3 ^ 3 = 27个测试用例 - 对于个别测试用例,你只需要3 * 3 + 2 ^ 3 = 17个测试用例。此时,您可能会利用这样一个事实,即您的逻辑将X个体验证结果与每个验证必须通过的一般规则相结合,以便对整个验证通过进行适当的测试。

现在,验证任意参数组合的任务已经分解为为每个参数编写特定的验证器,然后将所有参数组合成一个结果。

使用这种方法,您的测试将变得更加容易,因为您将所有这些方面分成单个测试。