如何在日志记录器中对消息进行JUnit断言

310

我有一些测试代码,它调用Java记录器来报告其状态。 在JUnit测试代码中,我想验证是否已经在记录器中正确地生成了日志条目。以下是大致的代码:

methodUnderTest(bool x){
    if(x)
        logger.info("x happened")
}

@Test tester(){
    // perhaps setup a logger first.
    methodUnderTest(true);
    assertXXXXXX(loggedLevel(),Level.INFO);
}

我认为可以通过一个特别适应的日志记录器(或处理程序,或格式化程序)来实现这一点,但我更希望重用已经存在的解决方案。(而且,说实话,我不清楚如何从日志记录器获取logRecord,但假设那是可能的。)

33个回答

0
请使用下面的代码。我在我的Spring集成测试中也使用了同样的代码,用于记录日志。使用assertJobIsScheduled方法来断言在日志中打印的文本。
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;

private Logger rootLogger;
final Appender mockAppender = mock(Appender.class);

@Before
public void setUp() throws Exception {
    initMocks(this);
    when(mockAppender.getName()).thenReturn("MOCK");
    rootLogger = (Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
    rootLogger.addAppender(mockAppender);
}

private void assertJobIsScheduled(final String matcherText) {
    verify(mockAppender).doAppend(argThat(new ArgumentMatcher() {
        @Override
        public boolean matches(final Object argument) {
            return ((LoggingEvent)argument).getFormattedMessage().contains(matcherText);
        }
    }));
}

0
对于使用Google的Flogger日志记录API的任何人来说,以下是您可以编写的用于测试的日志后端的方法:
import com.google.common.flogger.backend.LogData
import com.google.common.flogger.backend.LoggerBackend
import com.google.common.flogger.backend.system.BackendFactory
import java.lang.RuntimeException
import java.util.logging.Level
import java.util.logging.Level.SEVERE
import java.util.logging.Level.WARNING

/** Stores information about logs that occurred during test execution. */
class LogCollection {
  val messages = mutableListOf<String>()
}

class TestLoggingBackendFactory : BackendFactory() {
  /** Gets a singleton logging backend. */
  override fun create(p0: String?) = TestLoggingBackend
}

/** Flogger logging backend that listens to new logs during the test. */
object TestLoggingBackend : LoggerBackend() {
  val logs = LogCollection()

  override fun getLoggerName(): String = "TestLoggingBackend"

  override fun isLoggable(level: Level): Boolean = level in setOf(SEVERE, WARNING)

  override fun log(logData: LogData) {
    handleError(e = null, logData)
  }

  override fun handleError(e: RuntimeException?, logData: LogData) {
    logs.messages.add(logData.literalArgument.toString())
  }
}

然后你需要在某个地方设置系统属性
systemProperty(
  "flogger.backend_factory",
  "my.package.here.TestLoggingBackendFactory#getInstance"
)

现在,您可以通过TestLoggingBackend.logs访问所有发生的日志。
我进一步将其转化为了一个带有Truth断言的JUnit规则,您可以在这里看到:LoggingRule.kt

Flogger对于您选择的后端是不可知的,因此您可以按照这个答案建议的方式进行操作,或者连接到底层的日志系统(JDK/Log4J2等)来拦截日志。实际上,我正在(业余时间)开发一个与所有常见后端无缝配合的后端不可知日志测试库。 - David
我刚刚添加了一个新的答案,使得这个答案已经过时了。"flogger-testing"库(https://github.com/hagbard/flogger-testing)比这个功能更强大,而且设置起来更容易。 - undefined

-1
如果您正在使用log4j2,则https://www.dontpanicblog.co.uk/2018/04/29/test-log4j2-with-junit/中的解决方案可以让我断言消息已被记录。
解决方案如下:
  • 将log4j appender定义为ExternalResource规则

    public class LogAppenderResource extends ExternalResource {
    
    private static final String APPENDER_NAME = "log4jRuleAppender";
    
    /**
     * Logged messages contains level and message only.
     * This allows us to test that level and message are set.
     */
    private static final String PATTERN = "%-5level %msg";
    
    private Logger logger;
    private Appender appender;
    private final CharArrayWriter outContent = new CharArrayWriter();
    
    public LogAppenderResource(org.apache.logging.log4j.Logger logger) {
        this.logger = (org.apache.logging.log4j.core.Logger)logger;
    }
    
    @Override
    protected void before() {
        StringLayout layout = PatternLayout.newBuilder().withPattern(PATTERN).build();
        appender = WriterAppender.newBuilder()
                .setTarget(outContent)
                .setLayout(layout)
                .setName(APPENDER_NAME).build();
        appender.start();
        logger.addAppender(appender);
    }
    
    @Override
    protected void after() {
        logger.removeAppender(appender);
    }
    
    public String getOutput() {
        return outContent.toString();
        }
    }
    
  • 定义一个使用您的ExternalResource规则的测试

    public class LoggingTextListenerTest {
    
        @Rule public LogAppenderResource appender = new LogAppenderResource(LogManager.getLogger(LoggingTextListener.class)); 
        private LoggingTextListener listener = new LoggingTextListener(); //     Class under test
    
        @Test
        public void startedEvent_isLogged() {
        listener.started();
        assertThat(appender.getOutput(), containsString("started"));
        }
    }
    
不要忘记将log4j2.xml作为src/test/resources的一部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接