2013-08-12 62 views
2

我正在寫一個測試,我想捕獲測試方法發送的STDOUT消息。這裏是我的代碼:如何保存輸出Log4j在JUnit測試中發送到STDOUT?

@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(); 
    } 
} 

不幸的是,batchLog總是空的,雖然我仍然可以讀取預期的文本到stdout。

將文本打印到STDOUT的方法實際上會捕獲異常。然後將它傳遞到Logger,如下所示:

log.warn(「發生錯誤:」,e);

其中e是我在我的測試中調用的方法中引發的異常。

Logger使用此appender打印其信息:org.apache.log4j.ConsoleAppender。沒有特別的配置適用於這個appender。

我錯過了什麼?

P.S: 這SO answer是有趣的,但因爲ConsoleAppender已經創建之前StandardOutputStreamLog rule可以充當它不會爲我工作。

的Java 6
Junit的4.10

+0

該方法如何打印文本? 'System.setOut'只改變'System.out'字段的值;它可能會使用字段的緩存值或使用其他一些寫入標準輸出的方式。 – Joni

+1

代碼工作正常,作爲獨立的主要方法。檢查你的JUnit設置和拆卸方法。問題在別的地方。 –

+0

無法用正常的System.out.println(...)重現此操作,如何將文本打印到標準輸出? – d0x

回答

7

我終於解決了我的問題!

我的第一次嘗試是錯誤的,因爲我的意見是:@Joni在他的評論中表示:ConsoleAppender已經創建,所以我太晚地重定向STDOUT。

由於異常消息由Logger處理,我決定添加一個WriterAppender以支持StringWriter以獲取消息。

這是我工作的解決方案:

@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(); 
     } 
} 
+0

好的,我喜歡! –

0

嘗試使autoflushing上的PrintStream真:

PrintStream ps = new PrintStream(baos,true); 
2

稍微偏離主題,但在情況下,一些人(像我一樣,當我第一次發現這個線程)可能有興趣通過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)); 
    } 
} 

免責聲明

  • 我開發了這個庫,因爲我無法找到適合自己需求的解決方案。
  • 目前僅提供log4j,log4j2logback的綁定,但我很樂意添加更多。
+0

有趣的工作! – Stephan

相關問題