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

Mockito测试中返回Optional.empty而非指定对象的问题排查

Mockito测试中findById返回null的解决办法

核心问题分析

你的测试代码存在两个关键冲突点:

  1. JUnit4环境下混用了JUnit5的注解(@TestInstance、@BeforeEach),导致测试生命周期异常
  2. 重复初始化Mock:@RunWith(MockitoJUnitRunner.class)已经会自动处理@Mock/@InjectMocks的初始化,手动调用MockitoAnnotations.openMocks(this)会重置Stub配置

修复方案一:标准JUnit4+Mockito写法

移除JUnit5注解,删除重复初始化代码,用JUnit4原生的@Before替代@BeforeEach:

@RunWith(MockitoJUnitRunner.class)
public class TaskServiceTestWithoutSpring {

    @Mock
    private TaskRepository taskRepository;

    @InjectMocks
    private TaskService taskService; // 不需要@Spy,除非你要监控服务自身方法

    @Before // JUnit4的前置方法注解
    public void init(){
        Task task = new Task();
        task.setId(4);
        task.setStatus(StatusEnum.IN_PROGRESS);
        task.setDescription("amazing task");

        // 常规Stub写法,两种方式都可用
        when(taskRepository.findById(anyInt())).thenReturn(Optional.of(task));
        // doReturn(Optional.of(task)).when(taskRepository).findById(anyInt());
    }

    // 必须添加实际测试方法
    @Test
    public void getTaskById_ShouldReturnExistingTask() {
        Task result = taskService.getTaskById(4);
        assertNotNull(result);
        assertEquals("amazing task", result.getDescription());
        // 验证仓库方法被调用
        verify(taskRepository).findById(4);
    }
}

修复方案二:如果需要Spy TaskService

如果你确实需要监控TaskService自身的方法(比如验证内部调用、Stub服务方法),要显式创建服务实例再包装Spy:

@RunWith(MockitoJUnitRunner.class)
public class TaskServiceTestWithoutSpring {

    @Mock
    private TaskRepository taskRepository;

    private TaskService spyTaskService;

    @Before
    public void init(){
        // 手动创建服务实例并注入Mock仓库
        TaskService taskService = new TaskService(taskRepository);
        // 包装为Spy代理
        spyTaskService = Mockito.spy(taskService);

        Task task = new Task();
        task.setId(4);
        task.setStatus(StatusEnum.IN_PROGRESS);
        task.setDescription("amazing task");

        when(taskRepository.findById(anyInt())).thenReturn(Optional.of(task));
    }

    @Test
    public void getTaskById_ShouldReturnTask() {
        Task result = spyTaskService.getTaskById(4);
        assertNotNull(result);
    }
}

关键注意事项

  • 确保TaskService有可访问的构造函数接收TaskRepository(比如public TaskService(TaskRepository taskRepository)),否则@InjectMocks无法完成注入
  • JUnit4中不要使用JUnit5的注解,两者生命周期逻辑不兼容
  • @RunWith(MockitoJUnitRunner.class)会自动处理所有Mock初始化,禁止重复调用MockitoAnnotations.openMocks(this),否则会清空已配置的Stub

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 18:42:44