【问题标题】:Unit testing asserts duplication单元测试断言重复
【发布时间】:2011-02-08 07:52:37
【问题描述】:

我正在研究 TDD 并在我当前的项目中进行试验。 我注意到我必须在我的测试中复制很多断言。 这是情况: 我有带有两个构造函数的 Order 类,第一个是默认的, 第二个有三个参数

Order(int customerId, int typeId, decimal amount)

在 OrderTests 类中,我正在检查作业是否运行良好

Assert.IsTrue(o.CustomerId == 5 && o.TypeId == 3 && amount == 500)

由于订单创建是复杂的过程,因此我有具有以下创建订单方法的订单服务类。

Order CreateOrder(int cusotmerId, int typeId, int amount, moreParams...)

OrderServiceTests 类对此方法进行了测试,我需要使用相同的断言来检查 Order 是否已在 CreateOrder 服务中正确创建。

Assert.IsTrue(o.CustomerId == 5 && o.TypeId == 3 && amount == 500)
  1. 在测试中有这样的重复可以吗?
  2. 在测试中提取具有相同断言的方法是否有意义,因为有时编号或重复的断言可能不止一个?还是这样的方法提取使测试不可读?

【问题讨论】:

  • 据我了解,您正在尝试测试对象创建。为什么你需要这样做?有什么复杂的逻辑吗?
  • 是的,正如我所写,创建对象图是一个复杂的过程
  • 假设 Order 不是纯值对象/数据结构(在这种情况下,它可能太微不足道而无法失败/修复):您可以编写一个使用反射的辅助方法 AreAttributesEqual(expectedOrder, actualOrder, listOfAttributesToCompare)。如果您想始终比较所有属性,那就更简单了。

标签: unit-testing tdd nunit


【解决方案1】:

如果您有多种创建对象的方法,您可能需要测试每个创建方法(即参数化构造函数和工厂方法)的对象状态。因此,复制断言是有意义的。

在测试通过后的重构过程中(永远记住口头禅:red-green-refactor),如果您不仅在生产代码中而且在测试中发现重复,那么您应该删除它,例如使用 Extract Method 重构。

[TestMethod]
public void if_parametrized_ctor_is_called_then_state_should_be_accordingly {
  var order = new Order(customerId, ...);
  ObjectPropertiesShouldBeSetTo(order, customerId, ...);
}

[TestMethod]
public void if_factory_method_is_called_then_state_should_be_accordingly {
  var order = myFactory.CreateOrder(customerId, ...);
  ObjectPropertiesShouldBeSetTo(order, customerId, ...);
}

// Extracted to remove code duplication
public void ObjectPropertiesShouldBeSetTo(Order order, int customerId, ...) {
  Assert.AreEqual(customerId, order.CustomerId);
  Assert.AreEqual(...);
}

如果您在一个 Assert 语句中检查多个条件(如您的示例中所示),事情会变得复杂。它降低了测试的可读性,如果任何一个条件失败,可能很难找到原因。

【讨论】:

    【解决方案2】:

    如果您需要为多个测试执行相同的对象验证,将这些 Assert 拆分为一个通用方法是减少重复的好方法。在上面的示例中,您可以有一个名为 AssertObjectIsValid 的方法,并将公共代码移到那里。

    关于您的示例断言的另一件事。在单个 Assert 中组合多个检查使得从故障中确定哪个属性有故障变得更加困难。如果您将它们拆分为单独的 Assert,并为每个 Assert 提供消息,则可以更轻松地跟踪错误(尤其是如果您使用 CruiseControl.Net 等持续集成服务器。)修改您的示例:

    Assert.IsTrue(o.CustomerID == 5, "CustomerID doesn't match expected");
    Assert.IsTrue(o.TypeId == 3, "TypeID doesn't match expected");
    Assert.IsTrue(amount == 500, "Amount doesn't match expected");
    

    【讨论】:

    • 我还要补充一点,使用 Assert.IsEqual(5, o.CustomerID); 可以提高断言失败的可读性,因为它会明确告诉您设置了什么 CustomerID而不是使用 Assert.IsTrue()
    • 嗯,我在一个断言中写了几个检查以减少断言的数量。在其他情况下,我将有 8 个断言仅用于创建对象(因为它将状态更改历史记录项添加到内部历史记录集合中)。嗯,有 8 个断言可能不是问题。
    • @bcarlso - 使用 IsEqual 的要点。 @Danil - 如果您有很多事情要验证,那么拥有大量断言就可以了。将一组 Assert 分组到一个通用方法中有时有助于减少行数。
    【解决方案3】:

    是的,单元测试经常导致代码重复。但这种重复是有价值的。这个想法是,如果你用两种方式写同样的东西,你不太可能两次都犯同样的错误。这意味着您将捕获很多愚蠢的错误。 (诚​​然,其中一半在您的单元测试代码中。)从您的代码中自动生成单元测试会导致所有代码重复,而好处却更少。

    【讨论】:

      猜你喜欢
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多