2016-01-13 76 views
5

在使用TDD我發現自己需要測試常數(最終)散列映射包含查找值(請參閱之所以這樣的混合物在UPDATE THE CASE)正確單元測試技術

參見下面

private static final Map<Integer,String> singleDigitLookup = new HashMap<Integer, String>(){{ 
     put(0,"Zero");put(1,"One");put(2,"Two");put(3,"Three");put(4,"Four");put(5,"Five");put(6,"Six");put(7,"Seven"); 
     put(8,"Eight");put(9,"Nine"); 
}}; 

隨着TDD強調每次測試一件東西,所以我開始叫我的班級驗證每個元素的有效性如下。

TEST風格1

@Test 
public void whenWordIsOneThenReturn1(){ 
    assertEquals(1, WordToIntegerConverter.toInteger("One")); 
} 

寫第三次測試後,我認爲這是非常可笑的,並創造了反向鍵值對暫時查找,並開始呼籲在一個循環中,如下測試。

TEST樣式2

@Test 
    public void whenWordIsZeroThroughNineReturnIntegerConversion(){ 
     HashMap<Integer, String> lookup = new HashMap<Integer, String>(){{ 
      put(0,"Zero");put(1,"One");put(2,"Two");put(3,"Three");put(4,"Four");put(5,"Five"); 
      put(6,"Six");put(7,"Seven");put(8,"Eight");put(9,"Nine"); 
     }}; 
     for(int i = 0; i < 10; i++) { 
      assertEquals(i, WordToIntegerConverter.toInteger(lookup.get(i))); 
     } 
    } 

我的問題是這樣的;使用樣式1進行單元測試更好嗎?還是使用樣式2更好?

我看到了兩者的優點和缺點。例如樣式1非常簡潔,只測試一件事情,並且更容易理解。除了做大量的輸入測試套件之外,對於風格1的缺點也會導致許多微不足道的測試。樣式2的優點是較少的單元測試。樣式2的缺點有點複雜,可能會測試多個東西,但我會爭論它唯一的測試一件事是恆定散列表的有效性。

UPDATE 我從這個問題已經收到了相當數量的反彈,所以讓我進一步解釋。它不是我本人關心的常量,而是驗證了我的代碼的不同情況。這是一個實踐問題(實踐TDD通過卡塔斯)而不是生產代碼。問題在於將數字轉換爲單詞,所以我在單元測試中關心的是確保我可以正確處理不同的可能數字。還有其他的常量,我不包括例如不斷存儲青少年數字(11,12,13 ...)和十位數(20,30,40 ...)。這裏很容易犯一個錯字。

+0

在我看來,樣式2(或樣式1)沒有任何問題。我有很多使用循環做出各種斷言的測試。只要我覆蓋我的所有代碼,我很高興。 –

+2

好像你可以將你的查找重構爲'enum'類型而不是使用'Map'。當你有枚舉時,你可能甚至不需要單元測試常量,因爲編譯器會爲你進行類型檢查。 –

+2

不需要測試,只是眼球,以確保它是正確的:) – ZhongYu

回答

10

方法#1完成了工作,只是有一個令人討厭的cut-n-pasteing數量。方法#2修正了這個問題,但代價是測試不是獨立的:如果一個測試失敗,下面的測試不會運行。修復一個測試只是爲了找到一堆新的失敗現在很煩人。您可以通過參數化的測試改進這一點,這裏的an example from junit's wiki:

@RunWith(Parameterized.class) 
public class FibonacciTest { 
    @Parameters 
    public static Collection<Object[]> data() { 
     return Arrays.asList(new Object[][] {  
       { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } 
      }); 
    } 

    private int fInput; 

    private int fExpected; 

    public FibonacciTest(int input, int expected) { 
     fInput= input; 
     fExpected= expected; 
    } 

    @Test 
    public void test() { 
     assertEquals(fExpected, Fibonacci.compute(fInput)); 
    } 
} 

的參數測試包括輸入/​​預期輸出對的集合,每對輸入和輸出得到傳遞到構造函數調用的測試並在新的測試實例上調用測試方法。循環保存在測試框架中並且不在測試中,並且每個測試獨立於其他測試成功或失敗。

+0

如果我們可以在特定的測試方法上調用Parameters集合,那將是非常好的。而不是在該參數化類中的每個方法上調用 –

+0

@Marquis:不確定聽起來如此好。我寧願保留在不同測試類中使用不同數據的測試。 –