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

如何用Mockito模拟Java控制台应用的Scanner?解决Mock失败问题

解决Java控制台应用中Scanner模拟与Mockito Final类报错问题

问题分析

  1. Mockito报错原因:java.util.Scanner是final类,默认Mockito不支持mock final类,因此抛出Mockito cannot mock/spy because : final class错误。
  2. 测试无效原因:待测试类中直接在内部初始化Scanner,测试代码中mock的Scanner并未注入到目标实例,实际运行时仍使用真实的System.in输入,完全没用到mock对象。

解决方案

第一步:重构待测试类,实现依赖注入

将Scanner通过构造函数注入,让类的依赖可替换,既不影响生产环境使用,又方便测试时传入自定义Scanner实例:

import java.io.InputStreamReader;
import java.util.Scanner;

// 避免使用Class作为类名,防止与java.lang.Class冲突
public class MyClass {
    private final Scanner scanner;

    // 供测试用的构造函数,传入自定义Scanner
    public MyClass(Scanner scanner) {
        this.scanner = scanner;
    }

    // 生产环境默认构造函数,使用System.in输入
    public MyClass() {
        this(new Scanner(new InputStreamReader(System.in)));
    }

    public void startScreen() {
        System.out.println("Do a");

        while (!scanner.hasNext("a")) {
            System.out.println("No valid option!");
            scanner.next();
        }
        String input = scanner.next();

        if (input.equals("a")) {
            System.out.println("a");
        }
    }
}

第二步:选择合适的测试方案

方案1:模拟输入流(无需mock,更简洁)

直接用ByteArrayInputStream模拟用户输入"a",让Scanner读取该输入流,完全避免mock final类的问题:

import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.util.Scanner;

class MyClassTest {

    @Test
    void testValidInput() {
        // 模拟用户输入"a",添加换行符匹配Scanner的读取逻辑
        String simulatedInput = "a\n";
        ByteArrayInputStream inputStream = new ByteArrayInputStream(simulatedInput.getBytes());
        Scanner testScanner = new Scanner(new InputStreamReader(inputStream));

        MyClass myClass = new MyClass(testScanner);
        myClass.startScreen();

        // 如需验证输出,可重定向System.out:
        // ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // System.setOut(new PrintStream(outputStream));
        // myClass.startScreen();
        // String output = outputStream.toString();
        // assert output.contains("Do a") && output.contains("a");
    }
}

方案2:开启Mockito Final类Mock支持(若偏好使用mock)

如果一定要用Mockito mock Scanner,需开启Mockito对final类的支持:

  1. 在项目src/test/resources/mockito-extensions目录下创建文件org.mockito.plugins.MockMaker,内容为:
    mock-maker-inline
    
  2. 编写测试代码:
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.Scanner;

class MyClassTest {

    @Test
    void testWithMockScanner() {
        Scanner mockScanner = Mockito.mock(Scanner.class);
        // 模拟hasNext("a")返回true,next()返回"a"
        Mockito.when(mockScanner.hasNext("a")).thenReturn(true);
        Mockito.when(mockScanner.next()).thenReturn("a");

        MyClass myClass = new MyClass(mockScanner);
        myClass.startScreen();

        // 验证Scanner的next()方法被调用一次
        Mockito.verify(mockScanner, Mockito.times(1)).next();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 08:33:26