JUnit测试中如何通过ByteArrayInputStream模拟Scanner.nextFloat()输入?
问题
在使用ByteArrayInputStream和System.setIn模拟System.in进行JUnit测试时,字符串类型的菜单项输入能正常处理,但float类型的价格参数无法正确转换输入的字符串值。
Service代码
public class DailyMenuServicesImpl implements DailyMenuServices { private MenuPrinter menuPrinter; public DailyMenuServicesImpl(){ this.menuPrinter = new MenuPrinterImpl(); } @Override public DailyMenu addMenuItemsToMenu(DailyMenu dailyMenu) { Scanner scanner = new Scanner(System.in); MenuItem menuItem = new MenuItem(); List<MenuItem> menuItemList = dailyMenu.getMenuItemList(); try { System.out.print("\nInsert menu item name: "); menuItem.setNames(scanner.nextLine()); System.out.print("Insert menu item description: "); menuItem.setDescription(scanner.nextLine()); System.out.print("Insert menu item image: "); menuItem.setImage(scanner.nextLine()); System.out.print("Insert menu item price:"); menuItem.setPrice(scanner.nextFloat()); // 此处为问题点,上方代码均正常工作 menuItemList.add(menuItem); dailyMenu.setMenuItemList(menuItemList); }catch (IllegalStateException exception){ System.out.println(exception.getMessage()); }catch (InputMismatchException exception){ System.out.println(new InputMismatchException("Menu item price must be number !!!").getMessage()); } return dailyMenu; } }
单元测试代码
@RunWith(MockitoJUnitRunner.class) public class DailyMenuServiceTest { private MenuPrinter menuPrinter; private DailyMenuServices dailyMenuServices; private final InputStream systemIn = System.in; private final PrintStream systemOut = System.out; private ByteArrayInputStream testIn; private ByteArrayOutputStream testOut; @Before public void init(){ dailyMenuServices = new DailyMenuServicesImpl(); testOut = new ByteArrayOutputStream(); menuPrinter = new MenuPrinterImpl(); System.setOut(new PrintStream(testOut)); } @After public void restoreSystemInputOutput(){ System.setIn(systemIn); System.setOut(systemOut); } @Test public void whenAddNewMenuItemsToMenu_returnNewDailyMenu(){ List<MenuItem> menuItemList = new ArrayList<>(); DailyMenu dailyMenu = new DailyMenu(menuItemList); provideInput("Pizza\nGood\nItaly\n4.56f"); dailyMenu = dailyMenuServices.addMenuItemsToMenu(dailyMenu); menuPrinter.printMenu(dailyMenu); String expected = "Names: Pizza - Price: 4.56$"; assertEquals(expected,testOut.toString()); } private void provideInput(String data){ testIn = new ByteArrayInputStream(data.getBytes()); System.setIn(testIn); } }
解决方法
问题根源
- 非法的输入格式:测试输入中的
4.56f是Java代码的float字面量语法,但Scanner.nextFloat()只识别标准十进制数字(如4.56),带f后缀会直接触发InputMismatchException。 - Scanner的换行残留:连续调用
scanner.nextLine()后,输入流的换行符会残留,虽然当前测试未触发问题,但后续若有更多输入操作会导致异常。
修正步骤
- 修改测试输入内容:去掉价格后的
f后缀,使用标准十进制格式:
provideInput("Pizza\nGood\nItaly\n4.56");
- 清理Scanner缓冲区(可选但推荐):在
scanner.nextFloat()后添加scanner.nextLine(),消费掉输入流中剩余的换行符,避免后续操作异常:
menuItem.setPrice(scanner.nextFloat()); scanner.nextLine(); // 清除换行符
- 匹配输出时消除空白差异:由于测试输出可能包含额外换行或空格,可通过
trim()统一处理:
assertEquals(expected.trim(), testOut.toString().trim());
内容的提问来源于stack exchange,提问作者David Moore
相关产品推荐
相关产品推荐

