2009-07-22 172 views
15

所以對於記錄從SO和其他網站在互聯網上最好的迴應似乎是:單元測試:記錄和依賴注入

void DoSomething() { 
    Logger.Log("Doing something!"); 
    // Code... 
} 

現在一般你避免靜電的方法,但在登錄的情況下(一個特例)這是最簡單和最乾淨的路線。在靜態類中,您可以通過配置文件/框架輕鬆注入實例,以獲得與DI相同的效果。

我的問題來自單元測試的角度。

在上面的示例代碼中,想象DoSomething()的要點是將兩個數字加在一起。我會爲這個罰款寫我的單元測試。記錄怎麼樣?

我會爲日誌記錄編寫單元測試(但爲記錄器本身使用模擬實例)嗎?我知道如果是這種情況,我將不得不編寫集成測試來證明記錄器實際寫入日誌文件,但我不確定。

以下測試驅動開發(我這樣做)單元測試將需要我來規定接口不?

有什麼建議嗎?

回答

33

就我個人而言,我非常虔誠地練習TDD/BDD,而且我幾乎從不測試日誌記錄。除了一些例外,日誌記錄不是開發人員的便利或可用性因素,而不是方法核心規範的一部分。它也傾向於比方法的實際語義具有更高的變化率,所以你只是因爲你添加了更多的信息記錄而結束了測試。

這可能是值得的一些測試,只是鍛鍊日誌子系統,但對於大多數應用程序,我不會測試每個類以特定方式使用日誌。

12

我只寫過幾個日誌單元測試。這是一種痛苦,要麼使生產代碼變得混亂(由於注入記錄器)或者臭味測試(用模擬代替靜態記錄器)。與McWafflestix不同,我經常沒有發現它後來值得付出努力。

你要多少真的想知道日誌記錄是否正在工作,超出了你通過其他(動手)測試所看到的內容?你可能想使用DI作爲日誌非常重要的偶然類,但除此之外,我只是不會打擾測試日誌記錄。

這是假設日誌具有調試性質 - 如果它是一個審計日誌或類似的東西(具有功能要求的東西),這是一個不同的問題。

+1

嗯,我一直在說,你從來不寫一行代碼,而無需編寫一個測試,以證明其價值(TDD)所以這是這背後的動機。 – Finglas 2009-07-22 20:53:35

+2

@Dockers,可能被引用到死亡中,但這裏是Kent Beck對此的承擔。我認爲這條線更適合初學者。正如Jon所說,一旦你知道自己在做什麼,你的確可以跳過這些事情。 http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests/153565#153565 – Yishai 2009-07-22 20:59:14

+0

優秀。我不知道這是在這裏,讀了肯特貝克斯的消息。我會檢查整個討論。乾杯。 – Finglas 2009-07-22 21:02:08

0

我想說寫一個測試單元測試可能是一個合理的事情;我之前做過這件事,結果比我最初預期的更有用。通常,單元測試日誌記錄可以很好地保證日誌記錄工具在單元測試時間工作,這可以很好地保證。我認爲單元測試的日誌並不重要,但是如果你有興趣,我懷疑它會是一件壞事。

0

就我個人而言,我認爲它過於單調測試日誌報表。但是如果你真的想這樣做,那麼模擬記錄器就可以工作,或者如果使用像log4j這樣的框架,可以編寫一個在測試運行期間使用的自定義appender。

4

大多數日誌記錄框架允許您爲組件提供自定義實現。您可以使用該配置機制來提供您自己的實現。


例如,Java的Log4J的允許你聲明定製appenders,這是負責「提供」一個LoggingEvent所的組件。

記錄器可以很容易地嘲笑和使用注射:

Appender appenderMock = EasyMock.createMock(Appender.class); 
/* expect */ appenderMock.doAppend(EasyMock.isA(LoggingEvent.class)); 
EasyMock.replay(appenderMock); 

Logger.getLogger(/* replace with your own */"My logger").addAppender(appenderMock); 

EasyMock.verify(appenderMock); 

這個測試只驗證日誌事件被髮送,但可以完善它用得多多EasyMock

0

我通常做的斷言上有什麼不記錄的單元測試記錄語句,但我檢查我的單元測試所採取的代碼路徑覆蓋記錄語句只是爲了確保在登錄例外,我沒有得到一個例外!

9

我會分裂記錄分爲三類:

1)的要求。某些系統需要進行日誌記錄才能進行審計,或者需要滿足項目的其他一些要求(例如應用程序服務器中的日誌記錄標準)。那麼這確實是一個要求,值得進行單元測試和驗收測試,以確保滿足要求。所以在這種情況下,可以測試日誌的確切字符串。

2)解決問題。如果你開始在質量保證或生產中出現奇怪狀態,你希望能夠跟蹤正在發生的事情。一般來說,如果這很重要(例如在一個嚴重線程化的應用程序中,狀態可能變得複雜,但不能通過已知步驟再現),那麼測試給定的狀態值最終記錄下來可能是有價值的(因此,不測試日誌的整個可讀性,只是某些事實進入)。即使該類稍後改變,該狀態仍然可能被記錄(以及附加狀態),因此測試和日誌記錄之間的耦合是合理的。所以在這種情況下,只有部分日誌記錄被測試(一個包含測試)。

3)發展援助。在很多情況下,我使用日誌作爲更強大的評論形式。你可以寫如下語句:

logger.debug("Extract the fifth instance of BLAH from the string " + s); 

這樣就可以記錄代碼,並在同一時間有一個有用的神器,如果你以往任何時候都需要調試是怎麼回事。在這種情況下,我根本不會進行單元測試,因爲給定語句的存在與否並不重要。

至於你必須測試100%所有東西的觀點,請參閱Kent Beck的回答here。我認爲「測試所有東西」對於初學者來說是一個很好的建議,因爲當你開始使用TDD時,誘惑將是不測試任何難以測試的東西,或者推動你考慮設計使其可測試,以及將其合理化爲不重要。但是一旦你確實知道自己在做什麼,並且欣賞測試的價值,那麼重要的是要平衡你正在做什麼和測試什麼。

0

我可能會爲記錄器本身設置一個單獨的單元測試體,以測試其與其他所有功能分開的各種功能。在使用記錄器的方法中,我只會測試記錄器是否被正確的參數調用(即期望它被調用)。例如,如果我有一個方法:

DoSomething() 
{ 
    if (FatalErrorOccurred) 
    { 
     Logger.Log("Fatal Error", ErrorLevel.Fatal); 
    } 
} 

我會寫一個測試,示出了記錄器記錄一個致命的錯誤消息時FatalErrorOccurred是真實的。當然,我不會測試錯誤信息本身的內容,因爲這很容易改變。

0

儘管我同意其他人不會將TDD應用於日誌記錄,但我會嘗試確保單元測試涵蓋包含日誌語句的所有代碼路徑。重要的是確保在運行單元測試時配置最高的詳細級別,以便執行所有日誌語句。

例如,下面的代碼有一個錯誤,只有在啓用調試級別跟蹤時纔會拋出FormatException。

if (logger.IsDebugEnabled) 
{ 
    string message = String.Format(CultureInfo.CurrentCulture, 
      "... {1} ...", someValue); 
    logger.Debug(message); 
}