我有一些正在测试的代码,它调用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,但假设这是可能的。)


当前回答

非常感谢这些(令人惊讶的)快速而有用的回答;他们让我找到了正确的解决方法。

我想要使用的代码库使用java.util.logging作为其记录器机制,我对这些代码感到不够熟悉,无法完全将其更改为log4j或记录器接口/facade。但基于这些建议,我“破解”了一个j.u.l handler扩展,这是一种享受。

下面是一个简短的总结。延长java.util.logging.Handler:

class LogHandler extends Handler
{
    Level lastLevel = Level.FINEST;

    public Level  checkLevel() {
        return lastLevel;
    }    

    public void publish(LogRecord record) {
        lastLevel = record.getLevel();
    }

    public void close(){}
    public void flush(){}
}

显然,您可以从LogRecord中存储您喜欢/想要/需要的任何数量,或者将它们全部推入堆栈,直到溢出。

在junit-test的准备过程中,你创建了一个java.util.logging.Logger,并添加这样一个新的LogHandler:

@Test tester() {
    Logger logger = Logger.getLogger("my junit-test logger");
    LogHandler handler = new LogHandler();
    handler.setLevel(Level.ALL);
    logger.setUseParentHandlers(false);
    logger.addHandler(handler);
    logger.setLevel(Level.ALL);

对setUseParentHandlers()的调用将使正常的处理程序静默,以便(对于这个junit-test运行)不会发生不必要的日志记录。做任何你的测试代码需要使用这个记录器,运行测试和assertEquality:

    libraryUnderTest.setLogger(logger);
    methodUnderTest(true);  // see original question.
    assertEquals("Log level as expected?", Level.INFO, handler.checkLevel() );
}

(当然,您可以将大部分工作移到@Before方法中,并进行各种其他改进,但这会使演示变得混乱。)

其他回答

这里有一个很好的和优雅的方法来解决这个问题: https://www.baeldung.com/junit-asserting-logs

检查这个库https://github.com/Hakky54/log-captor

在maven文件中包含库的引用:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>logcaptor</artifactId>
    <version>2.5.0</version>
    <scope>test</scope>
</dependency>

在java代码测试方法中,你应该包括以下内容:

LogCaptor logCaptor = LogCaptor.forClass(MyClass.class);

 // do the test logic....

assertThat(logCaptor.getLogs()).contains("Some log to assert");

实际上,您是在测试依赖类的副作用。对于单元测试,您只需要验证这一点

logger.info()

使用正确的参数调用。因此,使用一个模拟框架来模拟记录器,这将允许您测试自己类的行为。

在这里,模拟是一个选项,尽管这很困难,因为记录器通常是私有的静态final -所以设置模拟记录器不是小意思,或者需要修改被测试的类。

你可以创建一个自定义的Appender(或者其他什么名字),然后注册它——要么通过一个只测试的配置文件,要么通过运行时(在某种程度上,依赖于日志框架)。 然后,您可以获得该appender(如果在配置文件中声明,可以是静态的,如果在运行时插入它,则可以通过它的当前引用),并验证其内容。

在我的案例中,我解决了如下的问题:

Logger root = (Logger) LoggerFactory.getLogger(CSVTasklet.class); //CSVTasklet is my target class
    final Appender mockAppender = mock(Appender.class);
    root.addAppender(mockAppender); 

verify(mockAppender).doAppend(argThat((ArgumentMatcher) argument -> ((LoggingEvent) argument).getMessage().contains("No projects."))); // I checked "No projects." in the log