为什么要在单元测试中避免使用条件逻辑?

Iva*_*kov 6 java testing junit automated-tests unit-testing

想象一下,有以下课程:

public class Product {
   private String name;
   private double price;

   // Constructors, getters and setters
}

public class Products {
   private List<Product> products;

   // CRUD methods

   public double getTotalPrice() {
      // calculates the price of all products
   }
}
Run Code Online (Sandbox Code Playgroud)

我已经读过在单元测试中应该避免条件逻辑(如果和循环),但是不明白为什么,更重要的是如何理解.如何有效地测试我Products以不同的价格添加一些产品的情况,然后在getTotalPrice()不使用循环的情况下验证结果?

dka*_*zel 7

条件和循环的恐惧是双重的:

  1. 您的TEST代码中可能存在错误,因此测试运行不正确或更糟糕的是条件块内的断言未被断言.
  2. 它更难阅读.

其他人通过复制和粘贴回答了硬编码列表.我不喜欢这样,因为它不仅使测试变得混乱,而且使得以后重构变得更加困难.

如果代码与Invisible Arrow的答案相似:

@Test
public void testsTotalPriceAsSumOfProductPrices() {
    Products products = new Products(); // Or any appropriate constructor
    products.add(new Product("first", 10)); // Assuming such a constructor exists
    products.add(new Product("second", 20));
    products.add(new Product("second", 30));
    assertEquals("Sum must be eq to 60", 60, products.getTotalPrice());
}
Run Code Online (Sandbox Code Playgroud)

Product改变的构造函数,你将不得不在许多不同的地方更改所有测试代码.

我更喜欢制作辅助方法,使测试代码更具意图,并将重构期间要更改的位置数量保持在最低限度(希望只有一个).

这不容易阅读:

 @Test
public void testsTotalPriceAsSumOfProductPrices() {
    Products products = new Products(); 

    addProductsWithPrices(products, 10,20,30);

    assertEquals(60, products.getTotalPrice());

}

private static void addProductsWithPrices(Products products, Double...prices){
  for(Double price : prices){
     //could keep static counter or something
     //to make names unique
     products.add(new Product("name", price));
  }
}
Run Code Online (Sandbox Code Playgroud)

是的,这确实使用for循环.但如果您担心它有错误或辅助方法更复杂,您也可以为它们编写额外的测试!最终,您可能希望将这些辅助方法考虑到自己的类中,以便可以在其他测试中重用它们.

此外,您可以看到测试方法隐藏了使product(name)与测试无关所需的其他字段.我们的测试只关心价格,所以我们的助手只是组成名字,阅读测试的人不会被额外的参数搞糊涂.很明显,这个测试确保10 + 20 + 30 == 60.

最后,如果我们将价格从一个对象double改为另一个Currency对象,我们只需要在一个地方进行更改,我们的测试代码就是可读的.

 private static void addProductsWithPrices(Products products, Double...prices){
  for(Double price : prices){
     //could keep static counter or something
     //to make names unique
     products.add(new Product("name", Currency.valueOf(price)));
  }
}
Run Code Online (Sandbox Code Playgroud)