2013-02-06 45 views
1

假設您正在使用TDD開發產品。您逐漸添加測試,並以一種大方法結束。現在是重構的時候了,因此您可以用較小的方法分離該方法。例如;爲提取的方法添加單元測試

// Before refactoring. 
public void SomeMethod() 
{ 
    // ... 
    int sum = numbers.Sum(); 
    // ... 
} 

// After refactoring. 
public void SomeMethod() 
{ 
    // ... 
    int sum = GetSumOfNumbers(numbers); 
    // ... 
} 

private GetSumOfNumbers(int[] numbers) 
{ 
    return numbers.Sum(); 
} 

經過這一步後,您應該爲GetSumOfNumbers方法編寫測試嗎?我認爲當我們測試SomeMethod時,我們已經測試了GetSumOfNumbers。但與此同時,可能有其他方法使用GetSumOfNumbers,即使它適用於SomeMethod,它可能不適用於另一種方法。這將幫助我們更快地找到問題(因爲測試會給出更具體的錯誤)。但同時,也許這沒有用,並且增加了詳細程度。

您對此有何看法?在這個例子中,GetSumOfNumbers方法是私有的,所以如果你認爲它不應該被測試,因爲它是私人的,它應該被測試,如果它是公開的?

回答

4

你應該測試你的類的公共API。不要測試私有方法。這樣做會創建一個測試套件,它與您的課程緊密相連。你班級的每一個內部變化都會破壞你的測試套件中的一些測試。

如果您認爲您應該測試特定的私有方法 - 或許是因爲它執行一些複雜的或複雜的邏輯 - 這是一種氣味告訴你,這可能是時間移動該方法進入一個新的類。

+1

+1。我喜歡把它推出去的建議。 –

+0

正如我在問題的最後提到的那樣,如果我決定該方法最終應該公開並公開發布,該怎麼辦?我應該爲該方法編寫測試嗎? – hattenn

+1

@hattenn:當然,看到我的答案的第一句話:「你應該測試你的類的公共API」。如果您公開該方法,它將成爲公共API的一部分。 –

0

不,你不知道。

這個想法是,當你提取方法時,測試會覆蓋你的背部。無論如何你都不能測試私有方法(沒有反射)。

通過調用GetSumOfNumbers的測試方法,您將自動對其進行測試。測試覆蓋工具將證實這一點。

+0

關於coverage的一個詞:僅僅因爲一個方法被另一個方法調用,並不意味着該方法被覆蓋完成。測量覆蓋率有多種不同的方法。例如。如果你想要一些路徑覆蓋並且在私有方法中有一個開關,那麼只有一個呼叫不會測試它。 (或者如果你有類似'if(numbers!= null && numbers.Count> 0){...} else {return null或throw sth。''),那麼你有很大的機會讓你從未到達零件,如果你的程序被正確寫入,並且你只有錯誤情況的條件(其他人改變了類並且做錯了一個調用) – Offler

+0

我會同意這些其他公開的方法在稍後也被放入類中,需要進行單元測試。只是因爲有一種方法叫另一個,另一個沒有完全覆蓋! – Offler

+0

公平點。我不覺得有必要提及這種情況..也許我應該有嗎?:) –

0

我很想知道你在你的設計更可測試的過程中的一半。請注意,擁有更多的公共方法/爲幾乎所有私有方法創建一個類別絕非「總是更好」。

正常的方法,我跟隨而重構是完全一樣的,你正在服用:

  • 長方法適用重構「提取法」(1)

當你這樣做時,你應該能夠區分新的對象和你的小公共方法,只是委託工作私人方法。

  • 應用重構 「介紹爲私有方法類」(2)

如果它在您的設計/建築中有意義!使用依賴注入將對象引用傳遞給SuT(被測主題),以便可以嘲笑它的行爲。之後,您可以編寫代表您以前的私有方法的待創建類的新測試。

有一點我想補充。我相信這是一種不好的做法,只是因爲你不想爲它寫測試,所以將私人方法推廣到公共方法上。「我還認爲將方法從私人內部推廣到內部是一種不好的做法,只是爲了測試它們。我會走得更遠:我認爲允許測試項目訪問內部方法是一種不好的做法。對我來說,這都是設計缺陷的表現。這並不意味着這就是不這樣做的原因。只要是有意識的選擇,您可以在代碼中添加代碼氣味!在所有其他情況下:保持您的設計可測試性,以便您不必欺騙並將私有方法推廣到公共方法。

一個或許過於簡單的例子:

public class TheThing 
{ 
    private readonly ISummator _summator; 

    public TheThing(ISummator summator) 
    { 
     _summator = summator; 
    } 

    public void SomeMethod() 
    { 
     _summator.SumStuff(); 
    } 
}