如何使用junit测试log4j是否记录了警告?

Asa*_*saf 47 java logging junit unit-testing log4j

我正在测试一个方法,当出现问题时记录警告并返回null.
就像是:

private static final Logger log = Logger.getLogger(Clazz.class.getName());
....
if (file == null || !file.exists()) {
  // if File not found
  log.warn("File not found: "+file.toString());
} else if (!file.canWrite()) {
  // if file is read only
  log.warn("File is read-only: "+file.toString());
} else {
  // all checks passed, can return an working file.
  return file;
}
return null;
Run Code Online (Sandbox Code Playgroud)

我想测试junit是否发出了警告,除了返回null之外,在所有情况下(例如找不到文件,文件是只读的).
有任何想法吗?
谢谢,asaf :-)


UPDATE

我对Aaron的回答的实现(加上彼得的评论):

public class UnitTest {
...

@BeforeClass
public static void setUpOnce() {
  appenders = new Vector<Appender>(2);
  // 1. just a printout appender:
  appenders.add(new ConsoleAppender(new PatternLayout("%d [%t] %-5p %c - %m%n")));
  // 2. the appender to test against:
  writer = new StringWriter();
  appenders.add(new WriterAppender(new PatternLayout("%p, %m%n"),writer));
}

@Before
public void setUp() {
  // Unit Under Test:
  unit = new TestUnit();
  // setting test appenders:
  for (Appender appender : appenders) {
    TestUnit.log.addAppender(appender);
  }
  // saving additivity and turning it off:
  additivity = TestUnit.log.getAdditivity();
  TestUnit.log.setAdditivity(false);
}

@After
public void tearDown() {
  unit = null;
  for (Appender appender : appenders) {
    TestUnit.log.removeAppender(appender);
  }
  TestUnit.log.setAdditivity(additivity);
}

@Test
public void testGetFile() {
  // start fresh:
  File file;
  writer.getBuffer().setLength(0);

  // 1. test null file:
  System.out.println(" 1. test null file.");
  file = unit.getFile(null);
  assertNull(file);
  assertTrue(writer.toString(), writer.toString().startsWith("WARN, File not found"));
  writer.getBuffer().setLength(0);

  // 2. test empty file:
  System.out.println(" 2. test empty file.");
  file = unit.getFile("");
  assertNull(file);
  assertTrue(writer.toString(), writer.toString().startsWith("WARN, File not found"));
  writer.getBuffer().setLength(0);
}
Run Code Online (Sandbox Code Playgroud)

多谢你们,

Aar*_*lla 38

在单元测试的设置中:

  1. 获得相同的记录器
  2. 使其成为非添加剂
  3. 添加一个记住列表中消息的appender:

    public class TestAppender extends AppenderSkeleton {
        public List<String> messages = new ArrayList<String>();
    
        public void doAppend(LoggingEvent event) {
            messages.add( event.getMessage().toString() );
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 将appender添加到记录器

现在你可以调用你的代码了.测试后,您将在列表中找到所有日志消息.如果需要(messages.add( event.getLevel() + " " + event.getMessage() );),请添加日志级别.

tearDown(),再次删除appender并启用可加性.

  • 在调用`doAppend()`之后,没有什么能阻止`event`的变化.`toString()`将确保你得到一个快照. (3认同)

jos*_*sle 16

使用Mockito,您可以使用最少的样板代码测试测试期间发生的日志记录,一个简单的示例是:

@RunWith(MockitoJUnitRunner.class)
public class TestLogging {

  @Mock AppenderSkeleton appender;
  @Captor ArgumentCaptor<LoggingEvent> logCaptor;


  @Test
  public void test() {
    Logger.getRootLogger().addAppender(appender);

    ...<your test code here>...

    verify(appender).doAppend(logCaptor.capture());
    assertEquals("Warning message should have been logged", "Caution!", logCaptor.getValue().getRenderedMessage());
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 美丽的。我的 logback 版本(1.1.7)中缺少 `AppenderSkeleton`,所以我使用了 `Appender&lt;ILoggingEvent&gt;appender`... (2认同)

Hai*_*man 8

这篇文章中的例子非常有用,但我发现它们有点令人困惑.
所以我为上面添加了一个简化版本,并进行了一些细微的修改.

  • 我将我的appender添加到根记录器.

这样,默认情况下,假设加法为真,我不需要担心由于记录器层次结构而导致丢失事件.确保这符合您的log4j.properties文件配置.

  • 我是压倒性的,而不是做的事.

在AppenderSkeleton中附加处理级别过滤,所以我不想错过它.
如果级别正确,doAppend将调用append.

public class TestLogger {
    @Test
    public void test() {
        TestAppender testAppender = new TestAppender();

        Logger.getRootLogger().addAppender(testAppender);
        ClassUnderTest.logMessage();
        LoggingEvent loggingEvent = testAppender.events.get(0);
        //asset equals 1 because log level is info, change it to debug and 
        //the test will fail
        assertTrue("Unexpected empty log",testAppender.events.size()==1);               
        assertEquals("Unexpected log level",Level.INFO,loggingEvent.getLevel());
        assertEquals("Unexpected log message"
                        ,loggingEvent.getMessage().toString()
                        ,"Hello Test");
    }

    public static class TestAppender extends AppenderSkeleton{
        public List<LoggingEvent> events = new ArrayList<LoggingEvent>();
        public void close() {}
        public boolean requiresLayout() {return false;}
        @Override
        protected void append(LoggingEvent event) {
                      events.add(event);
          }     
    }

    public static class ClassUnderTest {
        private static final Logger LOGGER = 
            Logger.getLogger(ClassUnderTest.class);
        public static void logMessage(){
          LOGGER.info("Hello Test");
          LOGGER.debug("Hello Test");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

log4j.properties

log4j.rootCategory=INFO, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d %p  [%c] - %m%n
# un-comment this will fail the test   
#log4j.logger.com.haim.logging=DEBUG
Run Code Online (Sandbox Code Playgroud)


Pét*_*rök 7

Aaron解决方案的另一种选择是配置带有附加StringWriterWriterAppender.在测试结束时,您可以验证日志输出字符串的内容.

这样更容易实现(不需要自定义代码),但是在检查结果方面不太灵活,因为您只能将输出作为纯文本.在某些情况下,与Aaron的解决方案相比,验证输出更加困难.