开发者

How to make log4j error() calls throw an exception in jUnit tests?

开发者 https://www.devze.com 2023-02-25 00:14 出处:网络
I have a Java project being tested with JUnit (mix of Junit 3 and 4 style), where the classes under test might log a log4j error.I would like to make a unit test fail if such an error is logged.

I have a Java project being tested with JUnit (mix of Junit 3 and 4 style), where the classes under test might log a log4j error. I would like to make a unit test fail if such an error is logged.

Is there a generic way to configure either log4j or the unit test infrastructure to make any call to a log4j error() method in the code under test throw a runtime exception and therefore fail the test? AOP might be one way, but I'm interested in other possibilities too.

The inte开发者_如何学JAVAntion here is to weed out places in code where log4j error() is being used incorrectly. That is, when an error is logged but no exception or error handling has occurred, either it's not really an error, or it is and should be raised.

eg:

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
    }
}

Update: This is what the solution I'm using currently looks like. It is based on sbridges' answer (added to test class):

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());
         }
    }
};

Then in setUp:

Logger.getRootLogger().addAppender(crashAndBurnAppender);

And tearDown:

Logger.getRootLogger().removeAppender(crashAndBurnAppender);


You can create a new Appender that throws an AssertionError when you log at error level.

Something like,

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

In your test do,

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

Edit : as Ralph pointed out, remove the TestAppender after you finish the test.


Log4j2 solution

I just stumbled upon this, but since I am using version 2 of Log4j, I had to adapt the code. It took me quite a while, since most resources are about a confusing mess of configuration factories, configurators, logger contexts and whatever. What I did may not be the cleanest or official way, but it's simple.

Definition of such a utility class with two public static methods:

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);
        }
    }
}

Ideally, you call enableThrowOnLog() within a JUnit 4 @BeforeClass method, and disableThrowOnLog() within a @AfterClass method, so that the changes to the global log system are properly undone.

0

精彩评论

暂无评论...
验证码 换一张
取 消