You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JUnit测试执行中途需用户键盘输入的Java方法

问题根因

测试阻塞的核心原因有两个:

  • 代码执行顺序错误:System.setIn() 重定向标准输入的逻辑必须在调用读取输入的被测方法之前执行。现有代码先调用了myclass.readInt(),方法启动后直接读取原生System.in等待键盘输入,后续的重定向逻辑根本没有执行机会,自然会卡住。
  • 被测代码存在隐藏缺陷:方法内调用scan.close()时,会连带关闭底层绑定的全局System.in流,即使当前用例跑通,同测试套件内其他依赖标准输入的用例也会抛出流已关闭的异常。
修复实现

核心步骤

  • 提前构造模拟输入流,完成System.setIn()重定向后,再调用被测方法
  • 测试执行前备份原生标准输入流,测试结束后恢复,避免污染全局测试环境
  • 移除被测方法中对绑定System.in的Scanner的close调用,防止全局输入流被意外关闭

可直接运行的代码示例

import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Scanner;
import static org.junit.Assert.assertEquals;

// 修正后的被测类
public class MyClass{
  public int readInt() {
      Scanner scan = new Scanner(System.in);
      System.out.print("Enter number: ");
      int num = scan.nextInt();
      // 移除scan.close(),避免关闭全局System.in
      return num;
  }
}

public class MyClassTest {
    // 备份原生标准输入流,用于测试后恢复
    private final InputStream originalSystemIn = System.in;

    @Test
    public void testReadInt() {
        // 第一步:先构造模拟输入,完成重定向
        String mockInput = "4";
        InputStream mockInputStream = new ByteArrayInputStream(mockInput.getBytes());
        System.setIn(mockInputStream);

        // 第二步:重定向完成后调用被测方法,不会阻塞
        MyClass myclass = new MyClass();
        int calcResult = myclass.readInt();

        // 第三步:结果断言
        assertEquals(4, calcResult);

        // 第四步:恢复原生标准输入
        System.setIn(originalSystemIn);
    }
}

无法修改被测代码时的兼容方案

如果业务代码中已经写死Scanner关闭逻辑,无法直接修改,可以通过匿名内部类重写模拟流的close方法,屏蔽关闭操作,避免全局流被销毁:

InputStream mockInputStream = new ByteArrayInputStream("4".getBytes()) {
    @Override
    public void close() {
        // 空实现,不执行实际流关闭操作
    }
};
System.setIn(mockInputStream);

如果测试类中有多个依赖模拟输入的用例,可以把流备份、恢复的逻辑抽到@Before/@After(JUnit4)或@BeforeEach/@AfterEach(JUnit5)生命周期方法中,减少重复代码。

内容的提问来源于stack exchange,提问作者devOn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 08:09:16