可通过System.setIn()替换System.in为ByteArrayInputStream实现控制台输入模拟,需注意换行符匹配、读取方法特性及测试前后流的保存与恢复。

可以通过 System.setIn() 将标准输入流(System.in)替换为自定义的 InputStream,从而在单元测试中模拟用户在控制台输入的内容。这种方式适用于测试依赖 Scanner、BufferedReader 或直接调用 System.in.read() 的代码,无需真实交互,实现可重复、自动化的输入控制。
准备模拟输入数据
将预设的输入内容转为字节流,常用方式是使用 ByteArrayInputStream 包装字符串(注意换行符需与目标平台匹配,一般用 "\n" 即可):
- 单行输入:
new ByteArrayInputStream("hello".getBytes()) - 多行输入(含换行):
new ByteArrayInputStream("42\ntrue\napple".getBytes()) - 含空行或特殊字符时,确保字符串准确反映实际输入序列
在测试中安全重定向 System.in
必须在测试开始前调用 System.setIn(),并在测试结束后恢复原始输入流,避免影响其他测试:
- 保存原始流:
InputStream originalIn = System.in; - 设置模拟流:
System.setIn(new ByteArrayInputStream("100\n200".getBytes())); - 执行被测方法(如包含
new Scanner(System.in)的逻辑) - 还原流:
System.setIn(originalIn);(推荐放在@After或finally块中)
配合 Scanner 使用的注意事项
Scanner 有内部缓冲和行尾处理逻辑,模拟输入时需注意:
- 每调用一次
nextLine(),需提供对应一行;nextInt()/nextBoolean()不会消费换行符,后续nextLine()可能读到空行 —— 此时应在数字后额外加一个"\n" - 示例:模拟输入
123然后nextLine()得到"abc",应写成"123\nabc\n" - 建议统一用
nextLine()读取,再手动解析,可避免令牌分隔导致的意外行为
封装可复用的测试工具方法
为提升可读性和复用性,可封装辅助方法:
- 静态方法如
withInput(String input, Runnable testAction):自动保存/恢复流并执行测试逻辑 - JUnit 5 中可用
@ExtendWith自定义扩展,在生命周期内管理输入流 - 对复杂交互(如循环输入、条件分支),可构造含完整交互序列的字符串,按预期顺序触发各次读取
不复杂但容易忽略的是换行符和流关闭问题 —— System.in 本身不可关闭,但模拟流无需显式关闭;关键在于输入内容格式与读取方式严格匹配,否则测试会卡在阻塞读取上。

















