1
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);
}
4

2 回答 2

1

我看不出编写这种测试的任何理由或优势。如果类或方法不存在,您的测试无论如何都会失败。因此,您不会从编写它们中获得任何收益。

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

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

编辑:

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

于 2013-09-03T13:08:08.593 回答
1

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

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

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

所有这些测试也为我提供了一个很好的代码覆盖率指标,因为大多数代码库都经过测试,并且代码确实遍历了类文件中的所有行。但是,测试到这个级别并尝试实现高代码覆盖率指标,对于您的团队来说确实是一个选择,无论是否需要。

于 2013-09-03T14:33:22.427 回答