2015-01-10 134 views
4

想象一下,有以下類:爲什麼要避免單元測試中的條件邏輯?

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()的結果?

+0

爲什麼不只是明確地添加(說)3個不同的產品與三個陳述?無論如何,這可能比循環更簡單。 –

+0

我相信這不會成爲問題,但我想了解它背後的意義。如果我想用更多的物品測試它會怎樣?如果計算更復雜呢?通常,我想了解如何驗證在類中完成的操作,該操作使用項目列表計算特定值以及如何避免遍歷測試用例中作爲參數給出的所有項目。 –

+3

你爲什麼要迭代* test *代碼?你可以在* real *代碼中迭代,但在測試代碼中,你可能只是硬編碼或者將其明確爲5 + 10 + 20或其他。並不是說我認爲迭代在測試中一定是一個問題 - 儘管「如果」條件至少是罕見的。儘管如此,我還是建議不要太教條。我更關心在貨幣數據中使用'double'... –

回答

5

恐懼用條件的排斥和循環是雙重的:

  1. 你可能有您的測試代碼中存在一個錯誤,所以測試不能正常運行,或者更糟糕的是,條件塊內部的斷言未被聲明。
  2. 這很難閱讀。

其他人已經通過複製和粘貼來硬編碼列表。我不喜歡這樣做,因爲它不僅會混淆測試,而且會使得以後重構變得更加困難。

如果代碼是像無形箭的回答是:

@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構造改變,你就必須改變在許多不同的地方所有的測試代碼。

我更喜歡使幫助方法,使測試代碼更多的意圖揭示,並保持在重構過程中改變的地方的數量最少(希望只有一個)。

這難道不是更容易閱讀:

@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循環。但是如果你擔心它有缺陷或者輔助方法更復雜,你也可以爲它們編寫額外的測試!最終你可能想將這些輔助方法分解到他們自己的類中,以便它們可以在其他測試中重用。

此外,您可以看到測試方法隱藏了製作產品所需的其他字段(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))); 
    } 
} 
1

避免單元測試中的條件和循環的原因是因爲它們變得難以閱讀和維護。每個條件很可能代表它自己的測試場景,並且應該在單獨的單元測試中。然後每個測試將作爲功能的文檔或規範。查看測試的人員將確切知道預期/不期望的功能。

對於上述特定情況,您可以採用簡單的方法並通過向列表中添加一些產品來測試求和功能。

public class ProductsTest { 
    @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()); 
    } 
} 

在我看來,使用上述要麼創建/插入項的循環是沒有必要的,並增加了複雜性的考驗。

你當然可以有更多的測試,測試的邊界條件,如和溢出,負數等