0

我对单元测试和一般测试很陌生。我正在使用 phpUnit 进行开发,但由于我的问题更笼统/设计问题,实际环境不应该太重要。

我认为,尽可能具体地编写测试用例是一种很好的做法。例如(越晚越好):

assertNotEmpty($myObject);              // myObject is not Null 
assertInternalType('array', $myObject); // myObject is an array
assertGreaterThan(0, count($myObject)); // myObject actually has entries

如果这是正确的,这是我的问题

如果正在测试的对象的状态取决于外部源(即数据库),甚至一般情况下,在测试用例中编写一些流控制是一种公认​​的做法吗?

喜欢:

if (myObject !== null) {
    if (count(myObject) > 0) {
    // assert some Business Logic
    }
    else {
    // assert some different Business Logic  
    }
} 

测试用例中的这种流控制是可以接受的还是“代码味道”并且应该被规避?如果可以,是否有任何提示或做法,这里应该牢记在心?

4

2 回答 2

3

Paul 的回答解决了测试方法范围和断言,但您的问题代码暗示的一件事是,如果返回的对象具有值 X,您将测试 A,但如果它具有值 Y,则测试 B。换句话说,您的测试需要多个值并测试根据实际得到的不同的东西。

一般来说,如果你的每个测试都有一个单一的、已知的、正确的值,你会成为一个更快乐的测试者。您可以通过使用固定的、已知的测试数据来实现这一点,通常是通过在测试本身内部进行设置。以下是三种常见的路径:

  • 用所有测试使用的一组固定数据填充数据库。这将随着您添加更多测试和功能而发展,并且随着事情的发展使其保持最新可能会变得很麻烦。如果您有修改数据的测试,您需要在每次测试后重置数据或回滚更改。
  • 为每个测试创建一个简化的数据集。在setUp()测试期间,使用其特定数据集删除并重新创建数据库。它最初可以使编写测试更容易,但您仍然必须随着对象的发展更新数据集。这也可能使运行测试花费更长的时间。
  • 在不直接测试这些 DAO 时,对您的数据访问对象使用模拟。这允许您在测试中准确指定应该返回哪些数据以及何时返回。由于您没有测试 DAO 代码,因此可以将其模拟出来。这使得测试可以快速运行,并且意味着您不需要管理数据集。但是,您仍然必须管理模拟数据并编写模拟代码。根据您的喜好,有许多模拟框架,包括 PHPUnit 自己的内置框架。
于 2011-07-05T22:41:08.393 回答
2

在你的测试用例中有一些控制流是可以的,但一般来说,要明白,如果它们是不相交的,你的单元测试会工作得最好,也就是说,它们每个测试不同的东西。这很好的原因是,当您的测试用例失败时,您可以从失败的测试用例中准确地看到失败的原因,而不是需要深入到更大的测试用例中才能看到出了什么问题。通常的度量标准是每个单元测试用例的单个断言。也就是说,每条规则都有例外,这肯定是其中之一;在你的测试用例中有几个断言没有什么必然的错,特别是当测试用例场景的设置/拆卸特别困难时。但是,您要避免的真正代码异味是您有一个“测试”的情况

于 2011-07-05T20:18:32.850 回答