PHPUnit测试问题 - 如何对我的班级进行单元测试

时间:2010-01-20 15:51:51

标签: php unit-testing phpunit

我正在尝试进行单元测试,因为它引入了明显的积极因素,我正在尝试为前一天写的课程编写单元测试。 (我知道这与TDD相反,请耐心等待)

我的班级Image与其他一些人一起用于图像处理。

Image基本上包装GD图像资源并将数据与其一起存储。例如,Image的实例将始终包含它的当前状态,即调整大小时的新宽度/高度,原始图像数据等。

Image类还包含

的方法
  • 从文件,字符串数据或网址创建自己,例如$image->loadFromPath()
  • 从当前Image实例的属性创建新的GD图像资源,例如用于调整图像大小以保持背景透明度等。
  • 克隆GD图像资源以用于操作类

我正在努力解决的问题是如何使用PHPUnit正确地对此类进行单元测试。我已经完成了一些阅读,我对如何处理它有一些相互矛盾的想法,我不知道什么是正确的。我,

  1. 为班级的每个方法编写测试。我在某处读到了我应该测试的每一种方法。但是,有些方法会运行其他方法(我也可以这样添加),因此您将拥有一系列依赖关系。但我也读到每个单元测试应该独立于另一个。那么如果是这种情况我该怎么办?
  2. 将每个测试编写为类的使用路径。我还读到某个地方,每个测试应该代表您可以在课程中使用的1个路径/使用路径。因此,如果您涵盖所有用途,您最终将获得完整的代码覆盖率。
  3. 那么,哪一个是正确的,如果有的话?

3 个答案:

答案 0 :(得分:9)

应编写单元测试以评估类的公共接口。您的测试用例应该使用该类,因为您打算在程序中使用它。这里的想法是测试类的行为(预期,意外或边缘条件)。

您发布的两个想法都是正确的。从理论上讲,您应该有足够的测试用例(通过代码路由),以便运行类中的所有方法。

如前所述,100%的测试覆盖率是一个很好的目标,但并不总是切合实际。

另外,在GD的情况下,要小心编写测试GD功能的单元测试(它已经过测试,你不需要浪费时间再次测试它)。我想read up on在PHPUnit手册中使用PHPUnit的模拟和存根(以及模拟文件系统)。

以下是示例测试的样子:

public function testImageIsResized()
{
    $image = new Image();
    $image->loadFromPath('some/path');
    $image->resize(200, 300);
    $this->assertEquals(200, $image->getWidth());
    $this->assertEquals(300, $image->getHeight());
}

现在,根据图像类的预期行为,此测试可能没有问题,或者可能会失败,因为它期望新维度按比例约束到原始图像维度。但是我们没有显式调用在测试本身中检查该约束的内部方法。

答案 1 :(得分:5)

您可以使用covers annotation指定测试是否涵盖多种方法。因此,如果您的某个方法调用另一个方法,您只需将注释添加到测试的docblock中,它就会被添加到您的代码覆盖率统计中,例如

/**
 * @test
 * @covers MyClass::something()
 * @covers MyClass::_somethingElse()
 */
public function somethingWorksAsExpected()
{
    $this->assertSame($expected, $this->testObject->something());
}

对于个人项目,100%的代码覆盖率是可以的。但是,我在会议上看到过100%被怀疑是必要的会议。尽管有各种好处,但测试需要时间来编写,在预算项目中,仅测试80/20并省略应用程序的不加批判的低优先级功能就足够了。

关于如何测试你的课程,请看一下Behaviour Driven Development in the PHPUnit Manual的章节。在您的情况下,我会测试您在问题中描述的功能。

答案 2 :(得分:0)

斯蒂芬梅尔罗斯说:

  

然而,一些方法运行   其他人(我可以补充),所以你   然后有一连串的依赖。但是我   还读到每个单元测试应该   独立于其他

测试独立性不是关于不测试相同代码两次,而是关于一个测试的结果(或缺少结果)是否会影响另一个测试的结果。如果您的第一个测试插入一些数据然后在它可以删除该数据之前失败,那么您的第二个测试可能会得到与预期不同的结果。理想情况下,您应该能够以随机顺序运行测试,或运行一些而不是其他运行。