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


当前回答

正如前面提到的,您可以使用mock框架。为此,您必须在类中公开记录器(尽管我可能更倾向于使其包私有而不是创建公共setter)。

另一种解决方案是手工创建一个假记录器。您必须编写伪记录器(更多的fixture代码),但在这种情况下,我更喜欢使用模拟框架中保存的代码来增强测试的可读性。

我会这样做:

class FakeLogger implements ILogger {
    public List<String> infos = new ArrayList<String>();
    public List<String> errors = new ArrayList<String>();

    public void info(String message) {
        infos.add(message);
    }

    public void error(String message) {
        errors.add(message);
    }
}

class TestMyClass {
    private MyClass myClass;        
    private FakeLogger logger;        

    @Before
    public void setUp() throws Exception {
        myClass = new MyClass();
        logger = new FakeLogger();
        myClass.logger = logger;
    }

    @Test
    public void testMyMethod() {
        myClass.myMethod(true);

        assertEquals(1, logger.infos.size());
    }
}

其他回答

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

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

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

简单的方法

  @ExtendWith(OutputCaptureExtension.class)
  class MyTestClass { 
    
          @Test
          void my_test_method(CapturedOutput output) {
               assertThat(output).contains("my test log.");
          }
  }
Here is the sample code to mock log, irrespective of the version used for junit or sping, springboot.

import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import org.mockito.ArgumentMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.Test;

import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class MyTest {
  private static Logger logger = LoggerFactory.getLogger(MyTest.class);

    @Test
    public void testSomething() {
    ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
    final Appender mockAppender = mock(Appender.class);
    when(mockAppender.getName()).thenReturn("MOCK");
    root.addAppender(mockAppender);

    //... do whatever you need to trigger the log

    verify(mockAppender).doAppend(argThat(new ArgumentMatcher() {
      @Override
      public boolean matches(final Object argument) {
        return ((LoggingEvent)argument).getFormattedMessage().contains("Hey this is the message I want to see");
      }
    }));
  }
}

在类实现中不需要依赖硬编码的静态全局记录器,可以在默认构造函数中提供默认记录器,然后使用特定的构造函数设置对所提供的记录器的引用。

class MyClassToTest {
    private final Logger logger;
    
    public MyClassToTest() {
      this(SomeStatic.logger);
    };
    
    MyClassToTest(Logger logger) {
      this.logger = logger;
    };
    
    public void someOperation() {
        logger.warn("warning message");
        // ...
    };
};

class MyClassToTestTest {
    
    @Test
    public warnCalled() {
        Logger loggerMock = mock(Logger.class);
        MyClassTest myClassToTest = new MyClassToTest(logger);
        myClassToTest.someOperation();
        verify(loggerMock).warn(anyString());
    };
}