为什么要在单元测试中避免条件逻辑,以及如何避免? [英] Why should you avoid conditional logic in unit tests and how?

查看:148
本文介绍了为什么要在单元测试中避免条件逻辑,以及如何避免?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下,有以下几类:

Imagine having the following classes:

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
   }
}

我已经读过,在单元测试中应该避免使用条件逻辑(如果有和循环),但是并不清楚确切的原因,更重要的是如何做到的.如何在不使用循环的情况下有效地测试在Products中添加价格不同的某些产品,然后验证getTotalPrice()的结果的情况?

I have read that conditional logic(if's and loops) should be avoided in unit tests, but didn't understand exactly why and more importantly how. How can I efficiently test the scenario where I add some products in Products with different prices and then verify the result of getTotalPrice() without using loops?

推荐答案

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

The fear with conditionals and loops is two-fold:

  1. 您的TEST代码中可能存在错误,因此测试无法正常运行,或者更糟糕的是,没有断言条件块内的断言.
  2. 很难读.

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

Other people have answered to just hard code the list via copy and paste. I don't like that because not only does it clutter the test but it makes it harder to refactor later.

如果代码类似于Invisible Arrow的答案:

If the code was like in Invisible Arrow's answer:

@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());
}

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

And the constructor for Product changed, you would have to change all the test code in lots of different places.

我更喜欢使用辅助方法,以使测试代码更易于显示,并在重构过程中将更改的位置数量减至最少(希望只有一个).

I prefer to make helper methods that make the test code more intent revealing and keep the number of places to change during refactoring to a minimum (hopefully just one).

这不是更容易阅读吗?

 @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));
  }
}

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

Yes this does use a for loop. But if you are worried about it having bugs in it or if the helper methods are more complicated, you can write additional tests for them too! Eventually you may want to factor these helper methods out to their own classes so they can be reused in other tests.

此外,您可以看到该测试方法隐藏了制造与该测试无关的产品(name)所需的其他字段.我们的测试只在乎价格,因此我们的助手只是编造名字,阅读测试的人不会因多余的参数而感到困惑.显然,此测试可确保10 + 20 + 30 == 60.

In addition, you can see that the test method hides the other fields that are required to make products (name) that are irrelevant to the test. Our test only cares about prices so our helper just makes up names and the person reading the test doesn't get confused by extra parameters. It's clear that this test makes sure 10+20+30 ==60.

最后,如果我们将价格从double更改为某种Currency对象,则只需一次更改就可以了,我们的测试代码也一样可读.

Finally, if we ever change our price from a double to somekind of Currency object we only have to make the change in once place and our test code is just as readable.

 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)));
  }
}

这篇关于为什么要在单元测试中避免条件逻辑,以及如何避免?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆