可通过system.setout()重定向system.out至bytearrayoutputstream捕获输出,测试前保存原流并设置新流,测试后恢复;需注意线程安全、换行符、刷新及错误流分离等问题。

可以通过 System.setOut() 将标准输出(System.out)重定向到一个内存中的 PrintStream(例如基于 ByteArrayOutputStream),从而在自动化测试中捕获 println() 等输出内容,用于断言或日志校验。
创建可捕获的输出流
核心是用 ByteArrayOutputStream 包装一个新的 PrintStream,再通过 System.setOut() 替换原始输出流:
- 在测试开始前(如
@BeforeEach)保存原始PrintStream并设置新的重定向流 - 调用待测代码(它内部使用
System.out.println(...)) - 在断言前,将重定向流的内容转为字符串(
baos.toString(StandardCharsets.UTF_8)) - 测试结束后(如
@AfterEach)务必恢复原始System.out,避免影响其他测试
JUnit 5 中的典型用法示例
以 JUnit 5 为例,推荐使用 PrintStream + ByteArrayOutputStream 组合:
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
@BeforeEach
void setUp() {
System.setOut(new PrintStream(outContent));
}
@AfterEach
void tearDown() {
System.setOut(originalOut);
}
@Test
void testPrintOutput() {
MyClass.printHello(); // 假设该方法调用 System.out.println("Hello")
String output = outContent.toString(StandardCharsets.UTF_8).trim();
assertEquals("Hello", output);
}
注意事项与常见陷阱
实际使用中需注意几个关键点:
-
线程安全:
System.out是全局静态变量,多个测试并发执行时可能互相干扰;建议每个测试独占重定向,或确保测试串行运行 -
换行符差异:不同系统下
println()使用的换行符不同(\n或\r\n),建议用trim()或正则匹配,而非硬编码比对 -
未刷新内容:若被测代码只调用
print()未调用flush(),且重定向流未自动刷新,可能导致内容未写入ByteArrayOutputStream;可显式调用System.out.flush()或改用带自动刷新的PrintStream(OutputStream, true) -
异常与错误流:
System.setOut()不影响System.err;如需捕获错误输出,需单独对System.setErr()做同样处理
替代方案:更轻量或更健壮的选择
对于新项目或更复杂场景,可考虑以下方式:
- 改造被测类,将
PrintStream作为参数注入(依赖反转),测试时传入模拟流 —— 更易测、更解耦 - 使用
LogCaptor(来自net.bytebuddy:byte-buddy-agent生态)捕获日志框架输出(如 SLF4J),适用于已迁移到日志系统的代码 - JUnit 5 的
SystemExitSecurityManager类似机制不适用此处,但提醒:不要混淆setOut和setSecurityManager










