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