我需要为一个设计很差的旧应用程序编写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());
@dfa的答案是伟大的,所以我采取了进一步的步骤,使测试输出块成为可能。
首先,我用一个方法captureOutput创建了TestHelper,该方法接受匿名类CaptureTest。captureOutput方法执行设置和分解输出流的工作。当CaptureOutput的测试方法的实现被调用时,它可以访问为测试块生成的输出。
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() );
}
});
}
而不是重定向系统。out时,我将重构使用System.out.println()的类,方法是传递一个PrintStream作为合作者,然后使用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"));
讨论
这样,被测类就可以通过简单的重构进行测试,而不需要间接重定向标准输出或使用系统规则进行模糊拦截。
为出
@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);
}
使用ByteArrayOutputStream和System。setXXX很简单:
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;
@Before
public void setUpStreams() {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}
@After
public void restoreStreams() {
System.setOut(originalOut);
System.setErr(originalErr);
}
示例测试用例:
@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输出版本字符串,等等)
编辑:
这个答案的先前版本在测试后称为System.setOut(null);这就是NullPointerExceptions注释引用的原因。