如何验证(通过单元测试)错误堆栈是否打印在日志文件中?
How to verify (with unit test) that error stack is printed in the log file?
继续我写了一个单元测试来验证如果出错,堆栈将打印在日志文件中。
测试方法:
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
只是在某处定义了一个 expectation/criteria 来匹配存储的日志事件。
然后将其包裹在 Junit 规则中,以便将 Appender 添加到 Log4J 中更容易一些,我通过实现 TestRule
和扩展 Statement
来做到这一点,确保第一件事 Statement
s evaluate
方法做的是:
LogManager.getRootLogger().addAppender(ruleAppender);
LogManager.getRootLogger().setLevel(Level.ALL);
备注:
这可以在没有 JUnit 规则的情况下完成,只要在测试之前执行以上两行以确保将附加添加到 Log4J(仍然需要自定义附加器)。
我没有进入 JUnit 规则代码,因为我们可能应该转移到不支持规则的 JUnit 5,我没有进入 LogExpecation
因为这很容易找到你正在寻找的东西for 在日志事件列表中并且可以以适合您的域的方式表达,例如检查 Throwable
是否已记录,也许在 org.apache.log4j.spi.LoggingEvent
中查找适当的方法
继续
测试方法:
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
只是在某处定义了一个 expectation/criteria 来匹配存储的日志事件。
然后将其包裹在 Junit 规则中,以便将 Appender 添加到 Log4J 中更容易一些,我通过实现 TestRule
和扩展 Statement
来做到这一点,确保第一件事 Statement
s evaluate
方法做的是:
LogManager.getRootLogger().addAppender(ruleAppender);
LogManager.getRootLogger().setLevel(Level.ALL);
备注: 这可以在没有 JUnit 规则的情况下完成,只要在测试之前执行以上两行以确保将附加添加到 Log4J(仍然需要自定义附加器)。
我没有进入 JUnit 规则代码,因为我们可能应该转移到不支持规则的 JUnit 5,我没有进入 LogExpecation
因为这很容易找到你正在寻找的东西for 在日志事件列表中并且可以以适合您的域的方式表达,例如检查 Throwable
是否已记录,也许在 org.apache.log4j.spi.LoggingEvent
中查找适当的方法