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打印流(对于in
和err
)。 你可以重定向到打印流记录到一个字符串,然后检查? 这似乎是最简单的机制。
(我会主张,在某个阶段,将应用程序转换为一些日志框架 – 但我怀疑你已经意识到这一点!)
我没有重定向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)); } }
声明 :
- 我开发了这个库,因为我无法找到适合自己需求的解决方案。
- 目前只有
log4j
,log4j2
和logback
绑定可用,但我很乐意添加更多。
@dfa的答案很好,所以我更进一步,使测试块的输出成为可能。
首先,我用一个方法captureOutput
创建了captureOutput
,它接受了恼人的类CaptureTest
。 captureOutput方法执行设置和拆除输出流的工作。 当调用CaptureOutput
的test
方法时,它可以访问测试块的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); }