【问题标题】:Make Logback throw exception on ERROR level log events使 Logback 在 ERROR 级别的日志事件上抛出异常
【发布时间】:2023-04-10 02:28:02
【问题描述】:

在运行单元测试时,我希望在记录 ERROR 级别消息的任何测试中失败。使用 SLF4J/Logback 实现这一目标的最简单方法是什么?我想避免编写自己的 ILoggerFactory 实现。

我尝试编写自定义 Appender,但我无法通过调用 Appender 的代码传播异常,所有来自 Appender 的异常都会被捕获。

【问题讨论】:

    标签: unit-testing slf4j logback


    【解决方案1】:

    关键是写一个自定义的appender。你没有说你使用哪个单元测试框架,但是对于 JUnit,我需要做一些类似的事情(它比所有错误都复杂一点,但基本上是相同的概念),并创建了一个 JUnit @Rule 来添加我的 appender ,并且 appender 会根据需要使测试失败。

    我将此答案的代码放在公共领域:

    import ch.qos.logback.classic.Level;
    import ch.qos.logback.classic.Logger;
    import ch.qos.logback.classic.LoggerContext;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import ch.qos.logback.core.AppenderBase;
    import org.junit.rules.ExternalResource;
    import org.slf4j.LoggerFactory;
    import static org.junit.Assert.fail;
    
    /**
     * A JUnit {@link org.junit.Rule} which attaches itself to Logback, and fails the test if an error is logged.
     * Designed for use in some tests, as if the system would log an error, that indicates that something
     * went wrong, even though the error was correctly caught and logged.
     */
    public class FailOnErrorLogged extends ExternalResource {
    
        private FailOnErrorAppender appender;
    
        @Override
        protected void before() throws Throwable {
            super.before();
            final LoggerContext loggerContext = (LoggerContext)(LoggerFactory.getILoggerFactory());
            final Logger rootLogger = (Logger)(LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME));
            appender = new FailOnErrorAppender();
            appender.setContext(loggerContext);
            appender.start();
            rootLogger.addAppender(appender);
        }
    
        @Override
        protected void after() {
            appender.stop();
            final Logger rootLogger = (Logger)(LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME));
            rootLogger.detachAppender(appender);
            super.after();
        }
    
        private static class FailOnErrorAppender extends AppenderBase<ILoggingEvent> {
            @Override
            protected void append(final ILoggingEvent eventObject) {
                if (eventObject.getLevel().isGreaterOrEqual(Level.ERROR)) {
                    fail("Error logged: " + eventObject.getFormattedMessage());
                }
            }
        }
    }
    

    使用示例:

    import org.junit.Rule;
    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class ExampleTest {
        private static final Logger log = LoggerFactory.getLogger(ExampleTest.class);
    
        @Rule
        public FailOnErrorLogged failOnErrorLogged = new FailOnErrorLogged();
    
        @Test
        public void testError() {
            log.error("Test Error");
        }
    
        @Test
        public void testInfo() {
            log.info("Test Info");
        }
    }
    

    testError 方法失败,testInfo 方法通过。如果测试调用也记录错误的真实被测类,它的工作原理是一样的。

    【讨论】:

    • FailOnErrorAppender 类中定义的 fail() 方法在哪里
    【解决方案2】:

    日志框架通常设计为不会向用户抛出任何异常。另一种选择(除了 Raedwald 的答案)是创建一个自定义附加程序,当记录错误消息时将静态布尔标志设置为 true,在设置方法中重置此标志并在拆卸方法中检查它(或创建一个 JUnit重置/检查标志的规则)。

    【讨论】:

      【解决方案3】:

      因此,如果调用记录器的任何错误报告消息,您希望测试用例失败。

      1. 使用依赖注入将要测试的代码与它应该使用的记录器相关联。
      2. 实现一个实现 SLF4J 记录器接口的测试替身,它对大多数方法没有任何作用,但会为错误记录方法抛出一个 AssertionError。
      3. 在测试用例的设置部分,注入测试替身。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-11
        • 1970-01-01
        • 2014-03-31
        • 1970-01-01
        相关资源
        最近更新 更多