2013-10-09 104 views
8

它是一個代碼氣味來窺探正在進行單元測試的對象嗎?例如說我有一個LineCounter類,其職責是簡單地計算一個字符串中的行數。 -Mockito對被單元測試的對象進行間諜

class LineCounter { 
    public int getNumLines(String string) { 
     String metadata = getStringMetadata(string); 

     // count lines in file 
     return numLines; 
    } 

    /** Expensive operation */ 
    protected String getStringMetadata(String string) { 
     // do stuff with string 
    } 
} 

現在我想寫一個JUnit 4測試這個測試getNumLines方法,同時嘲諷了昂貴getStringMetadata電話。我決定使用Mockito的間諜機制讓getStringMetadata返回一個虛擬值。

class LineCounterTests { 
    @Test public void testGetNumLines() { 
     LineCounter lineCounterSpy = Mockito.spy(new LineCounter()); 

     // Mock out expensive call to return dummy value.    
     Mockito.when(lineCounterSpy.getStringMetadata(Mockito.anyString()).thenReturn("foo"); 

     assertEquals(2, lineCounterSpy.getNumLines("hello\nworld"); 
    } 
} 

這是一個合理的事情嗎?我覺得測試一個間諜對象,而不是實際的類非常奇怪,但我真的不能想出一個反對它的理由。

+4

這可能是一個測試驅動你的代碼改進的情況。它看起來像獲取字符串元數據的工作應該被提取到另一個類,然後由'LineCounter'委託給另一個類。那時你已經創建了一個「接縫」,並且可以用更傳統的方式嘲笑這種依賴性(並且不會那麼臭) – millhouse

+0

@millhouse是的,完全同意這種方式看起來像是正確的方法。我想知道,在我的例子中,被測試的對象是間諜是否存在內在的錯誤? – Matthew

回答

3

我會回答這個問題分兩部分。首先,是的,它是代碼嗅覺來模擬或者窺探被測試的類。這並不意味着它不能正確地完成,但它有風險,應儘可能避免。

WRT你的具體例子,我會看到間諜如何正確使用,但這將是斷言,你有別的完全單元測試getStringMetadata。這就引出了一個問題,如果你已經在其他地方完全測試getStringMetadata,那麼你必須知道如何測試它,因此爲什麼不測試getNumLines沒有間諜。

所有這一切,millhouse是一個很好的觀點,但無論哪種方式,你必須單元測試昂貴的代碼。他的建議有助於隔離昂貴的代碼並確保您只需測試/運行一次。

+0

假設我在其他地方測試過'getStringMetadata'。我仍然想測試'getNumLines',而不必執行昂貴的'getStringMetadata'調用,因爲我已經知道該方法的行爲如預期。 @ millhouse的建議是有效的,我確實喜歡將'getStringMetadata'分解成另一個類的方法,但我原來的問題仍然存在。我的例子有什麼*錯誤*。或者問一個更好的方法是,爲什麼在測試'LineCounter'時更好的方法是將'getStringMetadata'提取到另一個類並嘲笑這種依賴? – Matthew

+0

你還提到,在測試中監視這個班級是有風險的。你能詳細說明一下嗎? – Matthew

+0

你問是否有「代碼味道」。我建議它這樣做。只要有人看到它,他們會想「等等,這裏發生了什麼」。這並不意味着它不能做得正確,只是一般而言它應該避免,因此會引起評論者的注意。 –

0

在這種情況下,存根被測方法調用的方法是完全合法的。它甚至是我能想到的獨立測試的唯一方法。你只是不想爲了測試的目的而將單一的方法提取到它自己的類中。

雖然存在殘留方法的副作用,如果stubbed方法有副作用,那麼也可能不足以存儲返回的值,那麼也必須存儲副作用。在副作用非常複雜的某些情況下,它可能甚至是一個反對它的原因,但這很可能是被測試類本身實現中的代碼異味的一個指示。

要回答你的問題,我覺得很容易找到理由,但很難找到理由反對。這是我每天使用的技術,它可以幫助我將小型方法中的實現分割開來,這些小型方法在完全隔離的情況下單獨進行測試,而且我還沒有看到任何限制。