2011-04-13 96 views
7

我有一個Java項目中使用JUnit(JUnit 3中的結構和4樣式),其中被測類可能會記錄一個錯誤log4j的測試。如果記錄這樣的錯誤,我想使單元測試失敗。如何使log4j error()調用在jUnit測試中引發異常?

有沒有配置任何的log4j或單元測試的基礎設施做出一個錯誤的log4j任何調用的代碼()方法測試拋出一個運行時異常,因此未通過測試的通用方法? AOP可能是一種方式,但我也對其他可能性感興趣。

這裏的意圖是,其中的log4j誤差()被不正確地使用淘汰地點代碼。也就是說,當記錄錯誤但沒有發生異常或錯誤處理時,它不是一個真正的錯誤,或者它應該被提出。

如:

public class MyTest extends TestCase { 
    public void testLogAnError() { 
     // Want to make this fail 
     new MyClass().logAnError(); 
    } 
} 

public class MyClass() { 
    static Logger logger = Logger.getLogger("foo"); 

    public void logAnError() { 
     // I'm logging an error, but not doing anything about it 
     logger.error("Something bad, or is it?"); 
     // TODO throw an exception or don't log an error if there isn't one 
    } 
} 

更新:這是我使用的解決方案目前的模樣。它是基於sbridges的回答(添加到測試類):

在安裝程序中
private static final Appender crashAndBurnAppender = new NullAppender() { 
    public void doAppend(LoggingEvent event) { 
     if(event.getLevel() == Level.ERROR) { 
       throw new AssertionError("logged at error:" + event.getMessage()); 
     } 
    } 
}; 

然後:

Logger.getRootLogger().addAppender(crashAndBurnAppender); 

和拆卸:

Logger.getRootLogger().removeAppender(crashAndBurnAppender); 

回答

14

您可以創建一個新的Appender拋出在錯誤級別登錄時出現AssertionError錯誤。

喜歡的東西,

class TestAppender extends AppenderSkeleton { 
    public void doAppend(LoggingEvent event) { 
     if(event.getLevel() == Level.Error) { 
       throw new AssertionError("logged at error:" + event.getMessage()); 
     } 
    } 
} 

在您的測試做,

Logger.getRootLogger().addAppender(new TestAppender()); 

編輯:拉爾夫指出,除去TestAppender完成測試後。

+2

您需要撤銷'Logger.getRootLogger()addAppender(新TestAppender());'測試後!因爲記錄器是靜態的,所以這個appender也將在下一次測試中預先發送,如果它沒有被刪除的話! - 您必須在'@ After'方法或finally語句中執行此操作,因爲您的測試中的「正常」代碼在異常後不會執行。 – Ralph 2011-04-13 05:55:06

+0

太棒了!我有這樣的事情正在運行...... – Rog 2011-04-13 23:25:19

0

Log4j2解決方案

我只是偶然發現了這一點,但因爲我使用的Log4j版本2,我不得不調整的代碼。我花了相當長的一段時間,因爲大部分資源都是關於配置工廠,配置器,記錄器上下文等等的混亂。我所做的未必是最乾淨的還是官方的方式,但它是簡單

有兩個公共靜態方法,一個工具類的

定義:

private static Appender appender; 

private static void enableThrowOnLog(Level level) { 
    // Downcast: log4j.Logger -> log4j.core.Logger 
    Logger rootLogger = (Logger) LogManager.getRootLogger(); 

    appender = new ThrowingAppender(level); 
    appender.start(); 
    rootLogger.addAppender(appender); 
} 

public static void disableThrowOnLog() { 
    // Downcast: log4j.Logger -> log4j.core.Logger 
    Logger rootLogger = (Logger) LogManager.getRootLogger(); 

    appender.stop(); 
    rootLogger.removeAppender(appender); 
    appender = null; 
} 

private static class ThrowingAppender extends AbstractAppender { 
    private final Level level; 

    public ThrowingAppender(Level level) { 
     // Last parameter: ignoreExceptions -- must be false or exceptions will be swallowed 
     super("ThrowingAppender", new AbstractFilter() {}, PatternLayout.createDefaultLayout(), false); 
     this.level = level; 
    } 

    @Override 
    public synchronized void append(LogEvent event) { 
     Level eventLevel = event.getLevel(); 
     if (eventLevel.isMoreSpecificThan(this.level)) { 
      String message = event.getMessage().getFormattedMessage(); 
      throw new AppenderLoggingException(eventLevel + " level was logged:\n> " + message); 
     } 
    } 
} 

理想情況下,你叫enableThrowOnLog()一個JUnit 4 @BeforeClass方法中,和disableThrowOnLog()一個@AfterClass方法中,這樣的變化對全局日誌系統被正確地撤消。