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

Java Mockito测试报错:Argument passed to verify is not a Mock如何解决?

解决Mockito测试报错与完善测试用例的思路

你遇到的Argument passed to verify() is of type ExamRepository and is not a mock!错误,根源确实是手动创建ExamRepository实例的那行代码,同时你的测试逻辑也存在一些需要调整的地方,我来一步步帮你梳理:


一、修复Verify报错的核心问题

错误原因拆解

  1. @InjectMocks的作用:这个注解已经会自动帮你创建ExamRepository的真实实例,并把你用@Mock标记的EntityManager注入到实例中,完全不需要你手动new。
  2. verify()的限制:这个方法只能用于Mock对象或Spy对象,而你手动new出来的ExamRepository是真实类实例,不是Mockito管理的Mock/Spy,所以会触发报错。
  3. 测试逻辑顺序错误:你现在直接验证getAllSkeletons执行了一次,但实际上根本没调用这个方法!正确的测试流程应该是:先调用被测方法,再验证依赖行为,最后断言结果。

修正后的基础测试类

public class ExamRepositoryTest {
    @InjectMocks
    private ExamRepository examRepository;

    @Mock
    private EntityManager entityManager;
    
    @Mock
    private TypedQuery<ExamSkeleton> typedQuery;

    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Test
    public void getAllSkeletons_ShouldReturnExpectedList() {
        // 1. 准备测试数据
        List<ExamSkeleton> expectedSkeletons = new ArrayList<>();
        expectedSkeletons.add(new ExamSkeleton());
        expectedSkeletons.add(new ExamSkeleton());

        // 2. Mock依赖的行为
        when(entityManager.createQuery(
            "SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s",
            ExamSkeleton.class
        )).thenReturn(typedQuery);
        when(typedQuery.getResultList()).thenReturn(expectedSkeletons);

        // 3. 调用被测方法
        List<ExamSkeleton> actualSkeletons = examRepository.getAllSkeletons();

        // 4. 断言返回结果符合预期
        assertEquals(expectedSkeletons.size(), actualSkeletons.size());
        assertEquals(expectedSkeletons, actualSkeletons);

        // 5. 验证依赖方法被正确调用
        verify(entityManager, times(1)).createQuery(
            eq("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s"),
            eq(ExamSkeleton.class)
        );
        verify(typedQuery, times(1)).getResultList();
    }
}

二、除了方法执行验证,还需要覆盖这些测试场景

针对getAllSkeletons方法,完整的测试需要覆盖正常流程、异常流程、边界场景三类:

1. 异常场景:当createQuery抛出IllegalArgumentException

此时方法应该返回空列表,并且错误日志被正确记录。

@Test
public void getAllSkeletons_WhenCreateQueryThrowsException_ShouldReturnEmptyList() {
    // Mock抛出异常
    when(entityManager.createQuery(anyString(), eq(ExamSkeleton.class)))
        .thenThrow(new IllegalArgumentException("Invalid JPQL statement"));

    // 调用被测方法
    List<ExamSkeleton> result = examRepository.getAllSkeletons();

    // 断言返回空列表
    assertTrue(result.isEmpty());

    // 验证日志被错误级别调用(注:原类中Logger是静态获取的,若要验证日志,需用PowerMockMock静态的LogManager,或者调整类设计让Logger可注入)
    // 示例PowerMock代码(需引入PowerMock依赖):
    // PowerMockito.mockStatic(LogManager.class);
    // Logger mockLogger = mock(Logger.class);
    // when(LogManager.getLogger(ExamRepository.class)).thenReturn(mockLogger);
    // verify(mockLogger, times(1)).error(any(IllegalArgumentException.class));
}

2. 边界场景:数据库中无任何ExamSkeleton记录

此时方法应返回空列表而非null,确保代码的空安全。

@Test
public void getAllSkeletons_WhenNoRecordsExist_ShouldReturnEmptyList() {
    when(entityManager.createQuery(anyString(), eq(ExamSkeleton.class))).thenReturn(typedQuery);
    when(typedQuery.getResultList()).thenReturn(Collections.emptyList());

    List<ExamSkeleton> result = examRepository.getAllSkeletons();

    assertTrue(result.isEmpty());
    verify(typedQuery, times(1)).getResultList();
}

3. 验证JPQL语句的正确性

在正常流程的测试中,我们用eq()匹配完整的JPQL语句,这能有效避免JPQL拼写错误(比如字段名、实体名写错)导致的隐性bug。


关键总结

  • 永远不要手动创建@InjectMocks标记的类实例,让Mockito自动处理依赖注入
  • verify()的核心作用是验证Mock依赖的交互行为,而非被测类的方法执行(除非被测类是Spy对象)
  • 测试用例要覆盖所有可能的分支路径,不能只测“正常跑通”的场景
  • 测试既要断言返回结果的正确性,也要验证依赖的调用是否符合预期

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:07:24