Unit testing 如何使用testng对日志消息进行单元测试

Unit testing 如何使用testng对日志消息进行单元测试,unit-testing,testng,log4j2,lombok,Unit Testing,Testng,Log4j2,Lombok,我们使用testng作为输出测试框架。我们还使用Lombok@Log4j2来实例化日志对象。我需要测试一些代码,它在特定条件下记录特定消息 我见过使用junit和Mockito的示例。但是我找不到如何在testng中实现它。切换到junit不是一个选项 编辑 我实现了一个类(CaptureLogger),它扩展了AbstractLogger import org.apache.logging.log4j.spi.AbstractLogger; public class CaptureLogge

我们使用testng作为输出测试框架。我们还使用Lombok@Log4j2来实例化日志对象。我需要测试一些代码,它在特定条件下记录特定消息

我见过使用junit和Mockito的示例。但是我找不到如何在testng中实现它。切换到junit不是一个选项

编辑 我实现了一个类(CaptureLogger),它扩展了AbstractLogger

import org.apache.logging.log4j.spi.AbstractLogger;

public class CaptureLogger extends AbstractLogger {
    ...
}
我无法将其连接到测试类的记录器

CaptureLogger customLogger = (CaptureLogger) LogManager.getLogger(MyClassUnderTest.class);
生成错误消息:

java.lang.ClassCastException: org.apache.logging.log4j.core.Logger cannot be cast to CaptureLogger
我发现LogManager.getLogger返回的是Logger接口,而不是Logger对象(实现Logger接口)


如何创建CaptureLogger的实例?

只要您使用Lombok生成记录器,您就无法使用给定的工具在源代码本身的级别上做很多工作。例如,如果放置
@Log4j2
注释,它将生成:

private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
这行代码已经编译好了

您可以尝试使用PowerMockito模拟LogManager.getLogger方法,但我并不真正喜欢这种工具。尽管如此,这是一个可行的方向

使用框架本身有两种方法

一种方法(我不太熟悉Log4j2,但它应该提供这种功能——我在很多年前做了类似于log4j1.x的事情)是提供您自己的logger实现,并在Log4j2配置级别将其与logger工厂相关联。 现在,如果您这样做,那么lombok生成的代码将返回您的logger实例,该实例可以记住在不同级别记录的消息(这是您必须在logger级别实现的自定义逻辑)

然后记录器将有一个方法
public List getResults()
,您将在验证阶段调用以下代码:

   public void test() {
     UnderTest objectUnderTest = ...
     //test test test

     // verification
     MyCustomLogger logger = (MyCutomLogger)LogManager.getLogger(UnderTest.class);

     List<String> results =  logger.getResults();
     assertThat(results, contains("My Log Message with Params I expect or whatever");

   }

只要您使用Lombok生成日志,您就无法使用给定的工具在源代码本身的级别上做很多工作。例如,如果放置
@Log4j2
注释,它将生成:

private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
这行代码已经编译好了

您可以尝试使用PowerMockito模拟LogManager.getLogger方法,但我并不真正喜欢这种工具。尽管如此,这是一个可行的方向

使用框架本身有两种方法

一种方法(我不太熟悉Log4j2,但它应该提供这种功能——我在很多年前做了类似于log4j1.x的事情)是提供您自己的logger实现,并在Log4j2配置级别将其与logger工厂相关联。 现在,如果您这样做,那么lombok生成的代码将返回您的logger实例,该实例可以记住在不同级别记录的消息(这是您必须在logger级别实现的自定义逻辑)

然后记录器将有一个方法
public List getResults()
,您将在验证阶段调用以下代码:

   public void test() {
     UnderTest objectUnderTest = ...
     //test test test

     // verification
     MyCustomLogger logger = (MyCutomLogger)LogManager.getLogger(UnderTest.class);

     List<String> results =  logger.getResults();
     assertThat(results, contains("My Log Message with Params I expect or whatever");

   }

您可以这样定义自己的appender:

public void test () {
     MyTestAppender app = createMemorizingAppender();
     associateAppenderWithLoggerUnderTest(app, UnderTest.class);
     UnderTest underTest = ...

     // do your tests that involve logging operations

     // now the verification phase:

     List<String> results =  app.getResults();
     assertThat(results, contains("My Log Message with Params I expect or whatever");


}
package com.xyz;
导入静态java.util.Collections.synchronizedList;
导入java.util.ArrayList;
导入java.util.List;
导入org.apache.logging.log4j.core.Appender;
导入org.apache.logging.log4j.core.Filter;
导入org.apache.logging.log4j.core.LogEvent;
导入org.apache.logging.log4j.core.appender.AbstractAppender;
导入org.apache.logging.log4j.core.config.plugins.Plugin;
导入org.apache.logging.log4j.core.config.plugins.PluginAttribute;
导入org.apache.logging.log4j.core.config.plugins.PluginElement;
导入org.apache.logging.log4j.core.config.plugins.PluginFactory;
@插件(name=“logstolistpappender”,category=“Core”,elementType=Appender.ELEMENT\u TYPE)
公共类LogsToListAppender扩展了AbstractAppender{
私有静态最终列表事件=synchronizedList(新ArrayList());
受保护的LogsToListAppender(字符串名称、筛选器){
super(名称、筛选器、空);
}
@插件工厂
公共静态LogsToListAppender createAppender(@PluginAttribute(“name”)字符串名,
@插件(“过滤器”)过滤器(过滤器){
返回新的LogsToListAppender(名称、过滤器);
}
@凌驾
公共无效附加(LogEvent事件){
事件。添加(事件);
}
公共静态列表getEvents(){
返回事件;
}
}
然后在类路径的根目录中创建一个名为
log4j2 logstolist.xml的文件,该类路径将引用appender:


最后创建TestNG测试:

package com.xyz;
导入静态org.testng.Assert.assertTrue;
导入org.apache.logging.log4j.LogManager;
导入org.apache.logging.log4j.core.config.Configurator;
导入org.testng.annotations.Test;
@试验
公共类日志{
静止的{
初始化(null,“classpath:log4j2 logstolist.xml”);
}
@试验
公共void测试日志(){
//调用生成日志的代码,例如。
LogManager.getLogger(LogsTest.class).trace(“Hello”);
assertTrue(LogsToListAppender.getEvents().size()>0);
}
}
如您所见,我们正在强制Log4j2使用带有
Configurator.initialize的自定义配置(null,“classpath:Log4j2 logstolist.xml”)static{}
block)

请记住,检查记录器名称对您也很有用,例如,
LogsToListAppender.getEvents().stream().filter(a->CLASS_,它生成_LOG.CLASS.getName().equals(a.getLoggerName()).collect(toList())


您可以使用
LogEvent::getMessage()
方法访问实际消息您可以这样定义自己的appender:

public void test () {
     MyTestAppender app = createMemorizingAppender();
     associateAppenderWithLoggerUnderTest(app, UnderTest.class);
     UnderTest underTest = ...

     // do your tests that involve logging operations

     // now the verification phase:

     List<String> results =  app.getResults();
     assertThat(results, contains("My Log Message with Params I expect or whatever");


}
package com.xyz;
导入静态java.util.Collections.synchronizedList;
导入java.util.ArrayList;
导入java.util.List;
导入org.apache.logging.log4j.core.Appender;
导入org.ap