java - 如何验证(通过单元测试)错误堆栈是否打印在日志文件中?

标签 java junit logback slf4j

继续中to this answer我编写了一个单元测试来验证如果出现错误,堆栈将打印在日志文件中。

测试方法:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private final Logger logger = LoggerFactory.getLogger(getClass());

public long getFq(String fi) {
    try {            
        return calcSomeThing(fi.toLowerCase());
    } catch (Exception e) {
        logger.error("unable to calculate SomeThing.  Error: "
                , e);
        return -1;
    }
}

单元测试:

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.slf4j.LoggerFactory;

@Test
public void getFileQualifier() {

    // get Logback Logger
    Logger logger = (Logger) LoggerFactory.getLogger(QService.class);

    // create and start a ListAppender
    ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
    listAppender.start();

    // add the appender to the logger
    // addAppender is outdated now
    logger.addAppender(listAppender);

    // call method under test
    QService.getFq(null);

    // JUnit assertions
    List<ILoggingEvent> logsList = listAppender.list;
    Assert.assertEquals("unable to calculate SomeThing.  Error: ", logsList.get(0)
            .getFormattedMessage());
    Assert.assertEquals(Level.ERROR, logsList.get(0)
            .getLevel());

    Assert.assertEquals("java.lang.NullPointerException: null", logsList.get(1)
            .getMessage());
    Assert.assertEquals(Level.ERROR, logsList.get(1)
            .getLevel());

    Assert.assertThat("(QService.java", containsString(logsList.get(2)
            .getMessage()));
    Assert.assertEquals(Level.ERROR, logsList.get(2)
            .getLevel());
}

好吧,虽然我可以看到堆栈确实打印在日志文件中,但是单元测试失败了,因为 logsList 仅包含一个项目(第一个打印行 [无法计算 SomeThing。错误: ])。

java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1

为什么会发生这种情况以及如何测试它?

编辑

答案: 答案(均引用@Gavin的回答和评论,谢谢):

对于第一个问题(为什么会发生),答案是:

It looks to me that exceptions is stored separately from the message in the log event

关于第二个问题(如何测试),答案是:

to find what you are looking for in the list of log events and can be expressed in a manner suitable to your domain, e.g checking the that a Throwable was logged, perhaps looking in org.apache.log4j.spi.LoggingEvent for appropriate methods

最后,我的验证代码是:

Assert.assertEquals(logsList.get(0).getThrowableProxy().getClassName(), "java.lang.NullPointerException");

最佳答案

这就是我过去捕获日志消息的方式,这是基于一个似乎不再可用的旧博客(不是我写的)。

这是为 Java 7/8 和 Junit4 编写的相当古老的代码。

我会尽量保持简短:)

首先,您需要一个 Appender,最好扩展 AppenderSkeleton,例如:

public class RuleAppender extends AppenderSkeleton {
    private final List<LoggingEvent> loggingEvents = new ArrayList<>();

    protected RuleAppender() {
        super(true);

        this.setThreshold(Level.TRACE);
        setName("Rule Appender");
    }

    @Override
    public void close() {
        // No op in this case
    }

    @Override
    public boolean requiresLayout() {
        return false;
    }

    @Override
    protected void append(final LoggingEvent event) {
        loggingEvents.add(event);
    }

    public boolean hasEventsMeeting(LogExpectation logExpectation) {
         // Use the LogExpectation to determine if the list of log events contains what you want.
    }

    @Override
    public String toString() {
        return "RuleAppender";
    }
}

LogExpectation 只是在某个地方定义了一个期望/标准来匹配存储的日志事件。

然后将其包装在 Junit 规则中,以使将 Appender 添加到 Log4J 更容易一些,我通过实现 TestRule 并扩展 Statement 来做到这一点,确保Statementevaluate 方法所做的第一件事是:

LogManager.getRootLogger().addAppender(ruleAppender);
LogManager.getRootLogger().setLevel(Level.ALL);

注释: 这可以在没有 JUnit 规则的情况下完成,只要在测试之前执行上述两行以确保将附加添加到 Log4J(仍然需要自定义附加程序)。

我还没有进入 JUnit 规则代码,因为我们可能应该转向不支持规则的 JUnit 5,而且我还没有进入 LogExpecation 因为这只是为了找到你是什么在日志事件列表中查找,并且可以以适合您的域的方式表达,例如检查是否记录了 Throwable,也许在 org.apache.log4j.spi 中查找。 LoggingEvent 适用于适当的方法

关于java - 如何验证(通过单元测试)错误堆栈是否打印在日志文件中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62623403/

相关文章:

java - 使用 Java 运行 .sh

java - 查看来自哪些 jars 类

java - JUnit 4 没有捕获抛出的异常

java - Mockito:如何在测试中识别未使用的 .when()

java - Logback 找不到资源 logback.xml

Java 从不同的类中普遍加载整数

java - Java 的 for-each 是否为每次迭代调用一个嵌入式方法(返回集合)?

java - Junit 和消息框

java - 仅当存在警告或更严重级别的日志事件时才记录所有级别

java - Logback 滚动文件附加程序 - 我可以将多个日志压缩到一个文件中吗?