单元测试:特定测试和控制流程

时间:2011-07-05 20:14:08

标签: php unit-testing testing phpunit flow-control

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

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

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

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

在测试用例中编写一些流控制是否是公认的做法,如果测试的对象的状态取决于外部源(即DB) - 或者甚至是一般的?

像:

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

测试用例中的这种流量控制是否可以接受或者是“代码异味”并且应该被规避?如果没问题,是否有任何提示或做法,这里应该记住哪些?

2 个答案:

答案 0 :(得分:3)

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

一般而言,如果您的每个测试都有一个已知的正确值,那么您将是一个更快乐的测试人员。您可以通过使用固定的已知测试数据来实现此目的,通常通过在测试本身内部进行设置。以下是三条常见路径:

  • 使用所有测试使用的一组固定数据填充数据库。随着您添加更多测试和功能,这将逐渐发展,随着事态的发展,保持最新状态变得非常麻烦。如果您有用于修改数据的测试,则需要在每次测试后重置数据或回滚更改。
  • 为每个测试创建简化的数据集。在setUp()期间,测试将丢弃并使用其特定数据集重新创建数据库。它最初可以使编写测试更容易,但是随着对象的发展,您仍然必须更新数据集。这也可以使测试运行时间更长。
  • 在不直接测试这些DAO时,为您的数据访问对象使用模拟。这允许您在测试中准确指定应返回哪些数据以及何时返回。既然你没有测试DAO代码,那么可以嘲笑它。这使得测试快速运行并且意味着您不需要管理数据集。但是,您仍然需要管理模拟数据,并编写模拟代码。根据您的偏好,有许多模拟框架,包括PHPUnit自带的内置框架。

答案 1 :(得分:2)

在你的测试用例中有一些控制流是可以的,但总的来说,要理解你的单元测试如果不相交就会最好,也就是说,他们每个测试都会测试不同的东西。这很好的原因是,当您的测试用例失败时,您可以准确地看到失败的测试用例失败,而不是需要深入更大的测试用例以查看出现了什么问题。通常的度量标准是每单元测试用例的单个断言。也就是说,每条规则都有例外,这当然是其中之一;在测试用例中有一些断言没有什么不妥,特别是当测试用例场景的设置/拆除特别困难时。但是,您要避免的真实代码气味是您有一个“测试”测试所有代码路径的情况。