Java井字棋测试问题:仅首个输入生效,后续输入无响应
Java井字棋测试问题:仅首个输入生效,后续输入无响应
兄弟,我仔细看了你的代码和问题描述,核心问题出在多个Scanner实例共享同一个System.in输入流上,咱们一步步拆解解决:
问题根源
你在InputSelector里每次创建实例都会new一个新的Scanner(System.in),而System.in是单向字节流——当第一个Scanner读取输入中的3之后,它会把流内内容缓存起来,后续新建的Scanner再读取时,流里已经没有剩余内容了,自然就抛出No line found错误。
手动运行时没问题是因为控制台输入是实时的,每次读取都会等待用户输入;但测试用的ByteArrayInputStream是一次性静态流,多个Scanner实例会把流的读取状态彻底搞乱。
解决方案
1. 复用同一个Scanner实例
重构InputSelector,让它接收外部传入的Scanner,而非每次自行新建,这样所有读取操作都用同一个Scanner,就能按顺序读取输入的3和2:
public class InputSelector { private final Scanner scanner; // 构造函数注入Scanner,方便测试时传入自定义输入流 public InputSelector(Scanner scanner) { this.scanner = scanner; } // 保留默认构造,供正常运行时使用 public InputSelector() { this(new Scanner(System.in)); } public int computerOrHuman() { return Integer.parseInt(scanner.nextLine()); } // 你的boardType()方法也要改成用这个scanner读取 public int boardType() { return Integer.parseInt(scanner.nextLine()); } }
2. 让依赖类接收统一的InputSelector
修改Board类,通过构造函数注入InputSelector,避免每次创建新实例:
public class Board { private char[][] values; private int rows; private int columns; List<Character> winnersX = new ArrayList<>(); List<Character> winnersO = new ArrayList<>(); private final InputSelector inputSelector; // 带参数的构造,供测试用 public Board(int rows, int columns, InputSelector inputSelector) { this.rows = rows; this.columns = columns; this.values = new char[rows][columns]; this.inputSelector = inputSelector; } // 默认构造,正常运行时使用 public Board(int rows, int columns) { this(rows, columns, new InputSelector()); } public int computerOrHuman() { return inputSelector.computerOrHuman(); } // ...其他方法 }
3. 调整测试代码,用统一的Scanner读取输入
测试时创建一个基于ByteArrayInputStream的Scanner,并传递给所有需要的类,确保输入被依次读取:
@Test void testRowX() { // given String testInput = "3\n2"; ByteArrayInputStream inputStream = new ByteArrayInputStream(testInput.getBytes()); Scanner testScanner = new Scanner(inputStream); InputSelector testInputSelector = new InputSelector(testScanner); // when OutputComputer outputComputer = new OutputComputer(); outputComputer.gameStart(); outputComputer.gameType(); int boardSize = testInputSelector.boardType(); // 读取第一个输入3 Board testBoard = new Board(boardSize, boardSize, testInputSelector); StartGame startGame = new StartGame(); // 建议修改runGame方法,直接传入Board实例,避免内部再创建新的InputSelector startGame.runGame(testBoard); String whoWin = "Won X"; // then // 注意原测试里的拼写错误:把"Won X"写成了"Wona X" assertEquals("Won X", whoWin); }
另外要注意:你原测试里的assertEquals有拼写错误,"Wona X"应该改成"Won X",不然测试肯定会失败。
额外提醒
如果不想大改代码,也可以在测试时只设置一次System.setIn,并且确保整个测试流程中只用一个Scanner实例,但构造注入的方式更灵活,也更符合测试驱动开发的思想,方便后续扩展测试场景。
备注:内容来源于stack exchange,提问作者Robert Wyszynski
相关产品推荐
相关产品推荐

