Java ByteArrayInputStream测试问题:System.in重置失败引发无行异常
问题:批量运行单元测试时System.in重置失效导致NoSuchElementException
在对Java应用进行单元测试、验证异常场景时,遇到如下问题:单独运行每个测试用例均通过,但批量运行时,System.in未正确重置,后续测试抛出java.util.NoSuchElementException: No line found异常。已使用@AfterEach注解尝试恢复System.in,但未达到预期效果。
测试代码如下:
public class RacingGameTest { private InputStream originalInputStream = System.in; void initializeStreams(String inputKey) { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputKey.getBytes()); System.setIn(byteArrayInputStream); } @AfterEach void restoreStreams() { System.setIn(originalInputStream); } @Test void 입력이_공백일_때_IllegalBlankException_던지기() { // Given final String ONLY_BLANK = " "; // When initializeStreams(ONLY_BLANK); // Then Assertions.assertThatThrownBy(racingGame::start).isInstanceOf(IllegalBlankException.class); } @Test void 이름이_누락되었을_때_IllegalBlankException_던지기() { // Given final String STRING_NAMES_CONTAIN_SPACE = "name1, ,name3"; // When initializeStreams(STRING_NAMES_CONTAIN_SPACE); // Then Assertions.assertThatThrownBy(racingGame::start).isInstanceOf(IllegalBlankException.class); } }
原因分析
核心问题在于测试复用了持有旧System.in引用的RacingGame实例或其内部的Scanner对象:
- 如果
RacingGame类将Scanner作为成员变量(初始化时绑定了初始的System.in),即使后续通过System.setIn()重置了输入流,Scanner依然持有已被读取完毕的旧流引用。 - 批量测试时,前一个测试的输入流被耗尽后,后续测试使用的
Scanner会因流已到末尾而抛出NoSuchElementException。
解决方案
1. 确保每个测试使用独立的RacingGame实例
在@BeforeEach注解方法中初始化RacingGame,保证每次测试都使用全新的实例,其内部的Scanner会绑定当前设置的System.in:
public class RacingGameTest { private InputStream originalInputStream; private RacingGame racingGame; @BeforeEach void setup() { // 每次测试前获取当前System.in作为原始流 originalInputStream = System.in; // 创建全新的RacingGame实例 racingGame = new RacingGame(); } void initializeStreams(String inputKey) { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputKey.getBytes()); System.setIn(byteArrayInputStream); } @AfterEach void restoreStreams() { System.setIn(originalInputStream); } @Test void 입력이_공백일_때_IllegalBlankException_던지기() { final String ONLY_BLANK = " "; initializeStreams(ONLY_BLANK); Assertions.assertThatThrownBy(racingGame::start).isInstanceOf(IllegalBlankException.class); } @Test void 이름이_누락되었을_때_IllegalBlankException_던지기() { final String STRING_NAMES_CONTAIN_SPACE = "name1, ,name3"; initializeStreams(STRING_NAMES_CONTAIN_SPACE); Assertions.assertThatThrownBy(racingGame::start).isInstanceOf(IllegalBlankException.class); } }
2. 修改RacingGame的实现逻辑
如果无法每次创建新实例,可修改start()方法,每次执行时重新创建Scanner,确保绑定最新的System.in:
public class RacingGame { public void start() { // 每次调用start时创建新的Scanner,使用当前的System.in Scanner scanner = new Scanner(System.in); // 后续读取输入的逻辑 // ... } }
总结
批量测试时System.in重置失效的本质是旧流引用被复用,只要确保每次测试的输入流依赖(如Scanner)都绑定最新的System.in,即可解决问题。
内容的提问来源于stack exchange,提问作者yong_ta_Park
相关产品推荐
相关产品推荐

