TDD,PHPUnit表示怀疑

时间:2013-09-03 11:17:02

标签: php unit-testing tdd phpunit agile

class MyClass {

    private $numeric;

    public function MyMethod($numeric)
    {

        if (! is_numeric($numeric)) {
            throw new InvalidArgumentException
        }

        $this->numeric = $numeric;

    }

}

1 - 有必要测试该类是否存在吗?

PHPUnit有几种自动运行的方法,例如assertPreConditions,setUp等。在这些方法中有必要使用assertTrue和class_exists检查类是否存在?例如:

protected function assertPreConditions()
{

    $this->assertTrue(class_exists("MyClass"), "The class does not exists.");

}

2 - 有必要检查方法是否存在?如果是,则该测试应该     是单独测试还是在每个单元测试中?

假设我们有一个只接受数字类型参数的方法,所以我们有两个测试,一个测试使用正确的参数,另一个使用不正确的方法期望异常,对吧?编写此方法的正确方法是......

这样:

public function testIfMyMethodExists()
{
    $this->assertTrue(method_exists($MyInstance, "MyMethod"), "The method does not exists.");
}

/**
* @depends testIfMyMethodExists
* @expectedException InvalidArgumentExcepiton
*/
public function testMyMethodWithAValidArgument()
{
    //[...]
}

/**
* @depends testIfMyMethodExists
* @expectedException InvalidArgumentExcepiton
*/
public function testMyMethodWithAnInvalidArgument()
{
    //[...]
}

还是这样?

public function testMyMethodWithAValidArgument()
{
    $this->assertTrue(method_exists($MyInstance, "MyMethod"), "The method does not exists.");
}

/**
* @expectedException InvalidArgumentExcepiton
*/
public function testMyMethodWithAnInvalidArgument()
{
    $this->assertTrue(method_exists($MyInstance, "MyMethod"), "The method does not exists.");
    //[...]
}

为什么?

3 - @covers和@coversNothing的真正目的是什么?

我正在阅读一份文件,说明PHPUnit的创建者Sebastian Bergmann作为良好实践,我们应该总是在方法和类中编写@covers和@coversNothing,并在xml中添加这些选项:

mapTestClassNameToCoveredClassName="true"
forceCoversAnnotation="true"

并在白名单中:

<whitelist addUncoveredFilesFromWhitelist="true">
    <directory suffix=".php"></directory>
</whitelist>

但真正需要它的是什么?

4 - 测试调用其他方法的构造函数的正确方法是什么?

似乎一切都很好,但不是在测试中。

即使我使用有效参数和无效参数进行测试,期望在方法“MyMethod”上出现异常,但如果我在构造函数中输入了错误的值,则不会发生这种情况(测试失败)。

如果我使用有效参数进行测试,则代码覆盖率不会导致100%。

public function __construct($numeric)
{
    $this->MyMethod($numeric);
}

2 个答案:

答案 0 :(得分:1)

我看不出写这种测试的任何理由和优势。如果类或方法不存在,那么测试无论如何都会失败。所以你写这些没有任何好处。

也许上面的一个例外(第1点)可能是你总是使用PHPUnit模拟框架构建SUT的情况(理论上你的SUT是模拟的情况,你不需要模拟其他方法,特别是测试是可能的,但我无法想象导致它的真实情况。)

在我看来,如果测试涵盖了完全包含在另一个测试中的流路 - 这意味着测试是多余的,没有必要。

修改

ad 3.因为您想知道特定单元测试调用了哪个确切的流路径。假设你有两个方法A和B.方法B调用方法A.如果你没有@covers注释测试方法B可以为方法A生成代码覆盖,你不能说你的单元是否测试A覆盖代码100%。

答案 1 :(得分:1)

我为应该存在的方法编写测试,以测试代码是否符合预期。我还测试InstanceOf()类(以及继承的类定义)以确保对象确实创建了要创建的内容。如果FOO()扩展了BAR(),那么我测试我创建的对象是InstanceOf(FOO)和InstanceOf(BAR)。如果类被更改为继承其他内容,或者删除了扩展,我的测试将再次通知开发人员检查代码以确保需要进行此更改。可能在FOO上调用了一些继承的函数,如果没有BAR的扩展,这段代码就会破坏。

我在要执行的各种代码路径上编写测试。因此,如果我希望函数在传递错误数据时抛出异常,我会为此编写一个测试。我这样做也有助于记录我们的源代码。测试显示了预期的行为。如果有人删除了异常(接受不同参数类型的新功能),则应更新测试以显示允许这样做。潜在地,参数的改变可能在其他地方引起问题。以这种方式测试可以确保多年后,我知道代码要求这是一个数字,如果我要更改参数类型,我肯定会仔细考虑代码。

使用测试驱动开发(TDD)可能会导致您不编写代码来抛出异常,因为您编写了测试,然后是代码以使测试通过。因此,您可能没有测试所有参数及其类型或值,但我尝试尽可能地验证合理的数据输入以尽量避免垃圾输入/垃圾输出(GIGO)问题。

所有这些测试也为我提供了良好的代码覆盖率指标,因为大多数代码库都经过了测试,代码确实逐步完成了类文件中的所有行。但是,测试到这个级别,并尝试实现高代码覆盖率指标,如果需要,您的团队真的可以选择。