可通过System.setIn()将标准输入重定向为ByteArrayInputStream模拟控制台输入,需按实际读取顺序拼接测试字符串并用\n分隔,测试后务必恢复原System.in,推荐在@BeforeEach/@AfterEach中管理。
可以通过 system.setin() 将标准输入重定向为一个预设的字符串流(如 bytearrayinputstream),让测试代码在调用 scanner.nextline() 或 bufferedreader.readline() 时自动读取你准备好的测试数据,从而实现控制台输入的自动化模拟。
准备测试输入数据
把多组测试用例按实际输入顺序拼成一个字符串,每行代表一次输入,用换行符 \n 分隔。注意末尾加不加换行不影响,但中间换行必须准确对应程序的读取逻辑。
- 例如:用户需依次输入“3”、“apple”、“banana”、“cherry”,则字符串为
"3\napple\nbanana\ncherry" - 若程序用
next()(跳过空白)而非nextLine(),空格和换行都可能被当作分隔符,此时可用空格分隔,如"3 apple banana cherry" - 含空行的场景(如某些题目要求空行结束),显式写入
"\n\n"
在测试中替换标准输入
使用 ByteArrayInputStream 包装输入字符串,并通过 System.setIn() 替换原始 System.in。注意测试结束后恢复原始输入流,避免影响其他测试。
- 保存原始
System.in:InputStream originalIn = System.in; - 设置新输入:
System.setIn(new ByteArrayInputStream(testInput.getBytes(StandardCharsets.UTF_8))); - 测试执行完毕后务必还原:
System.setIn(originalIn); - JUnit 5 中推荐在
@BeforeEach和@AfterEach中统一管理,或用try-finally确保还原
验证程序是否正确读取
不要只依赖程序“运行无异常”,要检查输出或返回值是否符合预期。常见做法是:
- 将原程序的
System.out也重定向到ByteArrayOutputStream,捕获输出内容做断言 - 如果被测逻辑封装为可调用方法(如
public static String processInput(Scanner sc)),直接传入新构造的Scanner(System.in),更易解耦和测试 - 对多组输入,可循环构造不同
testInput字符串,参数化运行(JUnit 5 的@ParameterizedTest)
注意事项与常见坑
Scanner 缓冲行为容易导致问题:比如先调 nextInt() 再调 nextLine(),nextInt() 不会消费换行符,导致第一个 nextLine() 读到空行。测试数据必须严格匹配这种行为。
- 建议统一使用
nextLine()读取所有输入,或在数字后手动加\n模拟回车 - 避免在测试中混用
System.in和自定义Scanner实例——确保 Scanner 构造时传入的是当前已 setIn 后的System.in - 字符编码默认为平台编码,建议显式指定
StandardCharsets.UTF_8防止中文乱码 - 多个并发测试时,
System.setIn()是 JVM 全局操作,务必隔离(JUnit 的ExecutionMode.SAME_THREAD或每个测试单独还原)

















