简体   繁体   English

如何保存输出Log4j在JUnit测试中发送到STDOUT?

[英]How to save output Log4j sends to STDOUT in a JUnit test?

I'm writing a test and I want to capture the messages sent on STDOUT by the tested method. 我正在编写测试,我想通过测试方法捕获STDOUT上发送的消息。 Here is my code: 这是我的代码:

@Test
public void testAction() throws IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
        CmdLineException, IOException {
    PrintStream originalSTDOUT = System.out;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);

    try {
        System.setOut(ps);

        // Call to the method that will print text to STDOUT...

        ps.flush();
        String batchLog = baos.toString("UTF-8");
        assertTrue("Invalid exception text !", batchLog.contains("my expected text..."));
    } finally {
        System.setOut(originalSTDOUT);//Restore original STDOUT
        originalSTDOUT.write(baos.toByteArray());// Write back to STDOUT, even if I comment this line, outputs go to STDOUT... 

        ps.close();
    }
}

Unfortunately, batchLog is always empty although I can still read the expected text to STDOUT. 不幸的是,虽然我仍然可以将预期的文本读取到STDOUT,但batchLog始终为空。

The method that will print text to STDOUT in fact catch an exception. 将文本打印到STDOUT的方法实际上会捕获异常。 It then passes it to a Logger like below: 然后它将它传递给Logger如下所示:

log.warn("An error has occured:", e); log.warn(“发生错误:”,e);

where e is the exception raised in the method I call in my test. 其中e是我在测试中调用的方法中引发的异常。

The Logger uses this appender for printing its messages: org.apache.log4j.ConsoleAppender . Logger使用此appender打印其消息: org.apache.log4j.ConsoleAppender No special configuration is applied to this appender. 没有特殊配置应用于此appender。

What am I missing ? 我错过了什么?

PS: This SO answer is interesting but it doesn't work for me since the ConsoleAppender is already created before the StandardOutputStreamLog rule can act. PS:这个SO答案很有意思,但它对我不起作用,因为在StandardOutputStreamLog规则可以行动之前已经创建了ConsoleAppender

Java 6 Java 6
Junit 4.10 Junit 4.10

I finally solved my problem ! 我终于解决了我的问题!

My first attempt was wrong because I redirect the STDOUT too late as @Joni stated in his comments: ConsoleAppender is already created. 我的第一次尝试是错误的,因为@Joni在他的评论中指出,我将STDOUT重定向太晚了: ConsoleAppender已经创建了。

Since, the exception message is handled by a Logger , I decided to add a WriterAppender backed by a StringWriter to it in order to get the message. 由于异常消息由Logger处理,因此我决定将StringWriter支持的WriterAppender添加到它以获取消息。

Here is my working solution: 这是我的工作解决方案:

@Test
public void testAction() throws IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
        CmdLineException, IOException {

        // Setup WriterAppender
        Writer w = new StringWriter();
        Layout l = new PatternLayout("%m%n");

        WriterAppender wa = new WriterAppender(l, w);
        wa.setEncoding("UTF-8");
        wa.setThreshold(Level.ALL);
        wa.activateOptions();// WriterAppender does nothing here, but I like defensive code...

        // Add it to logger
        Logger log = Logger.getLogger(ExceptionHandler.class);// ExceptionHandler is the class that contains this code : `log.warn("An error has occured:", e);'
        log.addAppender(wa);

        try {
             // Call to the method that will print text to STDOUT...

             String batchLog = w.toString();
             assertTrue("Invalid exception text !", batchLog.contains("my expected text..."));
        } finally {
             // Cleanup everything...
             log.removeAppender(wa);
             wa.close();
        }
}

Slightly off topic, but in case some people (like me, when I first found this thread) might be interested in capturing log output via SLF4J, commons-testing 's JUnit @Rule might help: 稍微偏离主题,但是如果有些人(像我这样,当我第一次找到这个帖子时)可能对通过SLF4J捕获日志输出感兴趣,那么commons-testing的JUnit @Rule可能会有所帮助:

public class FooTest {
    @Rule
    public final ExpectedLogs logs = new ExpectedLogs() {{
        captureFor(Foo.class, LogLevel.WARN);
    }};

    @Test
    public void barShouldLogWarning() {
        assertThat(logs.isEmpty(), is(true)); // Nothing captured yet.

        // Logic using the class you are capturing logs for:
        Foo foo = new Foo();
        assertThat(foo.bar(), is(not(nullValue())));

        // Assert content of the captured logs:
        assertThat(logs.isEmpty(), is(false));
        assertThat(logs.contains("Your warning message here"), is(true));
    }
}

Disclaimer : 免责声明

  • I developed this library since I could not find any suitable solution for my own needs. 我开发了这个库,因为我找不到适合自己需要的解决方案。
  • Only bindings for log4j , log4j2 and logback are available at the moment, but I am happy to add more. 目前只提供log4jlog4j2logback绑定,但我很乐意添加更多内容。

尝试在PrintStream上启用autoflushing true:

PrintStream ps = new PrintStream(baos,true);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM