使 Logback 在 ERROR 级别日志事件上抛出异常
Make Logback throw exception on ERROR level log events
当 运行 单元测试时,我希望在记录 ERROR 级别消息的任何测试中失败。使用 SLF4J/Logback 实现此目的的最简单方法是什么?我想避免编写自己的 ILoggerFactory 实现。
我尝试编写自定义 Appender,但我无法通过调用 Appender 的代码传播异常,所有来自 Appender 的异常都会被捕获。
因此,如果调用记录器的任何错误报告消息,您希望测试用例失败。
- 使用依赖注入将要测试的代码与它应该使用的记录器相关联。
- 实现一个实现 SLF4J 记录器接口的测试替身,它对大多数方法不执行任何操作,但会为错误记录方法抛出 AssertionError。
- 在测试用例的set-up部分,注入测试替身。
日志记录框架通常设计为不会向用户抛出任何异常。另一种选择(除了 Raedwald 的回答)是创建一个自定义附加程序,在记录错误消息时将静态布尔标志设置为真,在设置方法中重置此标志并在拆卸方法中检查它(或创建一个 JUnit规则 reset/check 旗帜)。
关键是写一个自定义appender。你没有说你使用哪个单元测试框架,但是对于 JUnit 我需要做一些类似的事情(它比所有错误稍微复杂一点,但基本上是相同的概念),并创建了一个 JUnit @Rule 添加了我的附加程序,并且 appender 根据需要未通过测试。
我将此答案的代码放在 public 域中:
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方法通过。如果测试调用也记录错误的真实 class-under-test,它的工作原理相同。
当 运行 单元测试时,我希望在记录 ERROR 级别消息的任何测试中失败。使用 SLF4J/Logback 实现此目的的最简单方法是什么?我想避免编写自己的 ILoggerFactory 实现。
我尝试编写自定义 Appender,但我无法通过调用 Appender 的代码传播异常,所有来自 Appender 的异常都会被捕获。
因此,如果调用记录器的任何错误报告消息,您希望测试用例失败。
- 使用依赖注入将要测试的代码与它应该使用的记录器相关联。
- 实现一个实现 SLF4J 记录器接口的测试替身,它对大多数方法不执行任何操作,但会为错误记录方法抛出 AssertionError。
- 在测试用例的set-up部分,注入测试替身。
日志记录框架通常设计为不会向用户抛出任何异常。另一种选择(除了 Raedwald 的回答)是创建一个自定义附加程序,在记录错误消息时将静态布尔标志设置为真,在设置方法中重置此标志并在拆卸方法中检查它(或创建一个 JUnit规则 reset/check 旗帜)。
关键是写一个自定义appender。你没有说你使用哪个单元测试框架,但是对于 JUnit 我需要做一些类似的事情(它比所有错误稍微复杂一点,但基本上是相同的概念),并创建了一个 JUnit @Rule 添加了我的附加程序,并且 appender 根据需要未通过测试。
我将此答案的代码放在 public 域中:
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方法通过。如果测试调用也记录错误的真实 class-under-test,它的工作原理相同。