如何使用Junit测试CoffeeMaker控制台程序的用户输入输出逻辑
咖啡机程序JUnit输入输出测试解决方案
错误原因说明
- 空指针异常:Main类中的
coffeeMaker是静态私有成员,你在测试类中实例化的CoffeeMaker对象和Main类持有的静态实例无关,直接调用Main.mainMenu()时Main的静态coffeeMaker未完成初始化,调用其方法就会触发空指针。 - 栈溢出错误:Main类所有操作执行完成后都会递归调用
mainMenu()形成死循环,你仅传入了一个"5"的输入,输入流读取完毕后后续读取操作会持续报错,触发无限递归调用mainMenu(),最终栈溢出。
解决方案
你需要做以下调整:
- 测试前后备份原始的
System.in和System.out,测试完成后立即恢复,避免污染其他测试用例 - 输入内容需要包含两条指令:先输入
5查询库存,再输入0退出程序,避免无限递归 - 捕获
System.exit(0)调用,避免测试进程被直接终止 - 直接调用
Main.main()方法触发初始化,无需自己在测试类中实例化CoffeeMaker - 输出匹配时可以选择校验整体输出包含库存内容,避免额外打印信息影响匹配结果
修改后的测试代码示例
import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import java.io.*; import java.security.Permission; public class CoffeeMakerTest { private InputStream originalIn; private PrintStream originalOut; private SecurityManager originalSecurityManager; private int exitCode; @Before public void setUp() { // 备份原始的输入输出和安全管理器 originalIn = System.in; originalOut = System.out; originalSecurityManager = System.getSecurityManager(); // 自定义安全管理器捕获System.exit请求 System.setSecurityManager(new SecurityManager() { @Override public void checkExit(int status) { exitCode = status; throw new SecurityException("Prevent System.exit from terminating test"); } @Override public void checkPermission(Permission perm) {} }); } @After public void tearDown() { // 恢复原始配置 System.setIn(originalIn); System.setOut(originalOut); System.setSecurityManager(originalSecurityManager); } @Test public void testCheckInventoryOption() { String lineSeparator = System.getProperty("line.separator"); // 构造输入:先输入5,再输入0退出 String input = "5" + lineSeparator + "0" + lineSeparator; System.setIn(new ByteArrayInputStream(input.getBytes())); ByteArrayOutputStream testOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(testOut)); // 调用main方法,会自动初始化Main的静态coffeeMaker try { Main.main(new String[]{}); } catch (SecurityException ignored) { // 捕获退出抛出的异常,不影响测试 } // 构造期望的库存输出 String expectedInventory = "Coffee: 15" + lineSeparator + "Milk: 15" + lineSeparator + "Sugar: 15" + lineSeparator + "Chocolate: 15" + lineSeparator; // 验证输出中包含期望的库存内容 assertTrue(testOut.toString().contains(expectedInventory)); } }
内容的提问来源于stack exchange,提问作者Edwin
相关产品推荐
相关产品推荐

