System.out.println()的JUnit测试

我需要为一个设计不好的旧应用程序编写JUnit测试,并将大量错误消息写入标准输出。 当getResponse(String request)方法行为正确时,它返回一个XML响应:

 @BeforeClass public static void setUpClass() throws Exception { Properties queries = loadPropertiesFile("requests.properties"); Properties responses = loadPropertiesFile("responses.properties"); instance = new ResponseGenerator(queries, responses); } @Test public void testGetResponse() { String request = "<some>request</some>"; String expResult = "<some>response</some>"; String result = instance.getResponse(request); assertEquals(expResult, result); } 

但是,当它变得格式不正确的XML或不理解请求时,它返回null并写入一些东西到标准输出。

有没有什么办法来断言在JUnit的控制台输出? 为了抓住这样的情况:

 System.out.println("match found: " + strExpr); System.out.println("xml not well formed: " + e.getMessage()); 

使用ByteArrayOutputStream和System.setXXX很简单:

 private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); @Before public void setUpStreams() { System.setOut(new PrintStream(outContent)); System.setErr(new PrintStream(errContent)); } @After public void cleanUpStreams() { System.setOut(null); System.setErr(null); } 

样本测试用例:

 @Test public void out() { System.out.print("hello"); assertEquals("hello", outContent.toString()); } @Test public void err() { System.err.print("hello again"); assertEquals("hello again", errContent.toString()); } 

我用这个代码来测试命令行选项(声明-version输出版本字符串等)

我知道这是一个古老的线程,但有一个很好的库来做到这一点:

系统规则

来自文档的示例:

 public void MyTest { @Rule public final SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Test public void overrideProperty() { System.out.print("hello world"); assertEquals("hello world", systemOutRule.getLog()); } } 

它也将允许你捕获System.exit(-1)和命令行工具需要测试的其他东西。

您可以通过setOut()设置System.out打印流(对于inerr )。 你可以重定向到打印流记录到一个字符串,然后检查? 这似乎是最简单的机制。

(我会主张,在某个阶段,将应用程序转换为一些日志框架 – 但我怀疑你已经意识到这一点!)

我没有重定向System.out ,而是通过传递PrintStream作为协作者,然后在生产环境中使用System.out测试中使用Test Spy来重构使用System.out.println()的类。

在生产中

 ConsoleWriter writer = new ConsoleWriter(System.out)); 

在测试中

 ByteArrayOutputStream outSpy = new ByteArrayOutputStream(); ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy)); writer.printSomething(); assertThat(outSpy.toString(), is("expected output")); 

讨论

通过这种方式,被测试的类可以通过简单的重构进行测试,而不需要间接地重定向标准输出或者使用系统规则来模糊地截取。

稍微偏离主题,但万一有人(像我,当我第一次发现这个线程)可能有兴趣通过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)); } } 

声明

  • 我开发了这个库,因为我无法找到适合自己需求的解决方案。
  • 目前只有log4jlog4j2logback绑定可用,但我很乐意添加更多。

@dfa的答案很好,所以我更进一步,使测试块的输出成为可能。

首先,我用一个方法captureOutput创建了captureOutput ,它接受了恼人的类CaptureTest 。 captureOutput方法执行设置和拆除输出流的工作。 当调用CaptureOutputtest方法时,它可以访问测试块的output generate。

TestHelper的源代码:

 public class TestHelper { public static void captureOutput( CaptureTest test ) throws Exception { ByteArrayOutputStream outContent = new ByteArrayOutputStream(); ByteArrayOutputStream errContent = new ByteArrayOutputStream(); System.setOut(new PrintStream(outContent)); System.setErr(new PrintStream(errContent)); test.test( outContent, errContent ); System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); System.setErr(new PrintStream(new FileOutputStream(FileDescriptor.out))); } } abstract class CaptureTest { public abstract void test( ByteArrayOutputStream outContent, ByteArrayOutputStream errContent ) throws Exception; } 

请注意,TestHelper和CaptureTest是在同一个文件中定义的。

然后在您的测试中,您可以导入静态captureOutput。 这里是一个使用JUnit的例子:

 // imports for junit import static package.to.TestHelper.*; public class SimpleTest { @Test public void testOutput() throws Exception { captureOutput( new CaptureTest() { @Override public void test(ByteArrayOutputStream outContent, ByteArrayOutputStream errContent) throws Exception { // code that writes to System.out assertEquals( "the expected output\n", outContent.toString() ); } }); } 

如果你使用的是Spring Boot(你提到你正在使用一个旧的应用程序,所以你可能不会,但它可能对别人有用),那么你可以使用org.springframework.boot.test.rule.OutputCapture以下列方式:

 @Rule public OutputCapture outputCapture = new OutputCapture(); @Test public void out() { System.out.print("hello"); assertEquals(outputCapture.toString(), "hello"); } 

你不想重定向system.out流,因为它重定向了整个JVM。 在JVM上运行的任何东西都可能会搞砸。 有更好的方法来测试输入/输出。 看着存根/嘲笑。

在使用JUnit时,不能直接使用system.out.println或使用记录器api进行打印。 但是如果你想检查任何值,那么你可以使用

 Assert.assertEquals("value", str); 

它会抛出下面的断言错误:

 java.lang.AssertionError: expected [21.92] but found [value] 

你的价值应该是21.92,现在如果你将使用下面这个值来测试你的测试用例。

  Assert.assertEquals(21.92, str); 

出去

 @Test void it_prints_out() { PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out)); System.out.println("Hello World!"); assertEquals("Hello World!\r\n", out.toString()); System.setOut(save_out); } 

对于错误

 @Test void it_prints_err() { PrintStream save_err=System.err;final ByteArrayOutputStream err= new ByteArrayOutputStream();System.setErr(new PrintStream(err)); System.err.println("Hello World!"); assertEquals("Hello World!\r\n", err.toString()); System.setErr(save_err); }