如何使用testng单元测试我的日志消息

时间:2019-05-31 04:58:39

标签: unit-testing testng log4j2 lombok

我们使用testng作为测试框架。我们还使用Lombok @ Log4j2实例化我们的日志对象。我需要测试一些代码,以便在某些情况下记录某些消息。

我已经看到了使用junit和Mockito的示例。但是我无法在testng中找到方法。不能切换到junit。

编辑

我已经实现了扩展AbstractLogger的类(CaptureLogger)

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的实例?

2 个答案:

答案 0 :(得分:1)

只要您使用Lombok生成记录器,就不能使用给定工具在源代码本身的水平上做很多事情。例如,如果放置@Log4j2注释,它将生成:

private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

此行已随附编译的代码。

您可以尝试使用PowerMockito模拟LogManager.getLogger方法,但是我真的不喜欢这种工具。请说明这一点,因为它可能是一个可行的方向。

有几种使用框架本身的方法。

一种方法(我不特别喜欢Log4j2,但是它应该提供此功能-多年前我对Log4j 1.x做过类似的事情)是提供您自己的logger实现并将其与logger工厂关联在Log4j2配置级别。 现在,如果您执行此操作,则由lombok生成的代码将返回您的logger实例,该实例可以存储在不同级别记录的消息(这是您必须在Logger级别实现的自定义逻辑)。

然后,记录器将具有方法public List<String> 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");

   }

我可以想到的另一种相似的方式是创建一个自定义附加程序,该附加程序将存储测试期间发送的所有消息。然后,您可以(通过声明或编程方式将该附加程序绑定到LogFactory.getLogger所获得的Logger中,用于测试的类(或其他类,具体取决于您的实际需求)。

然后让测试工作并进行验证-从log4j2系统获取对附加程序的引用,并使用某种public List<String> getResults()方法索取结果,除了附加方法外,附加程序上还必须存在什么内容必须遵守才能遵守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");


}

答案 1 :(得分:1)

你可以像这样定义你自己的 appender:

package com.xyz;

import static java.util.Collections.synchronizedList;

import java.util.ArrayList;
import java.util.List;

import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;

@Plugin(name = "LogsToListAppender", category = "Core", elementType = Appender.ELEMENT_TYPE)
public class LogsToListAppender extends AbstractAppender {

    private static final List<LogEvent> events = synchronizedList(new ArrayList<>());

    protected LogsToListAppender(String name, Filter filter) {
        super(name, filter, null);
    }

    @PluginFactory
    public static LogsToListAppender createAppender(@PluginAttribute("name") String name,
            @PluginElement("Filter") Filter filter) {
        return new LogsToListAppender(name, filter);
    }

    @Override
    public void append(LogEvent event) {
        events.add(event);
    }

    public static List<LogEvent> getEvents() {
        return events;
    }
}

然后在将引用 appender 的类路径的根目录中创建一个名为 log4j2-logstolist.xml 的文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="com.xyz" >
    <Appenders>
        <LogsToListAppender name="LogsToListAppender" />
    </Appenders>

    <Loggers>
        <Root level="TRACE">
            <AppenderRef ref="LogsToListAppender" />
        </Root>
    </Loggers>
</Configuration>

您应该特别注意(正确更新)属性 packages="com.xyz"(您的 appender 的包),否则它将不可用。有关详细信息,请查看 https://www.baeldung.com/log4j2-custom-appender

最后创建TestNG测试:

package com.xyz;

import static org.testng.Assert.assertTrue;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.config.Configurator;
import org.testng.annotations.Test;

@Test
public class LogsTest {

    static {
        Configurator.initialize(null, "classpath:log4j2-logstolist.xml");
    }

    @Test
    public void testLogs() {
        // call your code that produces log, e.g.
        LogManager.getLogger(LogsTest.class).trace("Hello");
        assertTrue(LogsToListAppender.getEvents().size() > 0);
    }
}

如您所见,我们正在强制 Log4j2 在类初始化时使用带有 Configurator.initialize(null, "classpath:log4j2-logstolist.xml"); 的自定义配置(static{} 块)。

请记住,检查记录器名称对您也很有用,例如LogsToListAppender.getEvents().stream().filter(a -> CLASS_THAT_PRODUCES_LOG.class.getName().equals(a.getLoggerName())).collect(toList());

您可以使用 LogEvent::getMessage() 方法访问实际消息