2011-04-13 52 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方法中,这样的变化对全局日志系统被正确地撤消。