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

Spring Boot 2.4.1中@Spy与@Autowired结合引发NotAMockException问题求助

解决Mockito的NotAMockException异常问题

你遇到的这个org.mockito.exceptions.misusing.NotAMockException异常,核心原因是你把Spring容器管理的真实Repository对象直接用@Spy标记了——SimpleJpaRepository是Spring Data JPA生成的代理实例,不是Mockito创建的mock/spy对象,当openMocks(this)执行时,Mockito会尝试将这个Spring代理对象当作自己的mock来处理,自然就会报错。

下面给你两种可行的解决方案,优先推荐第一种:

方案1:使用Spring Boot提供的@SpyBean注解(推荐)

Spring Boot专门提供了@SpyBean注解,用来Spy Spring容器中已存在的Bean,完美解决Spring代理与Mockito的冲突。你只需要把原来的@Spy + @Autowired替换成@SpyBean即可:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = TestServer.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ActiveProfiles("test")
@Transactional
class SampleServiceTest {
    // 替换@Spy + @Autowired为@SpyBean
    @SpyBean
    private TestRepo repo;
    
    @Mock
    SpecialTest test;
    @Mock
    SpecialTest1 test1;
    
    // 注意:这里是@InjectMocks,不是@InjectMock(拼写错误)
    @InjectMocks
    TestServiceTest testService;
    
    @BeforeEach()
    void setup () {
        openMocks(this);
        // 注意:两次when同一个方法返回不同值,最后一次会覆盖前一次,按需调整
        when(testService.getId()).thenReturn(test1);
    }
    
    @Test
    void test () {
        // 你的测试逻辑
    }
}

@SpyBean会自动从Spring容器中获取TestRepo的实例,创建对应的Spy对象后再放回容器,让Mockito和Spring能完美协作。

方案2:手动创建Spy对象(适合理解原理)

如果你不想用@SpyBean,也可以手动创建Spy实例并处理依赖注入:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = TestServer.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ActiveProfiles("test")
@Transactional
class SampleServiceTest {
    @Autowired
    private TestRepo realRepo;
    
    private TestRepo repoSpy;
    
    @Mock
    SpecialTest test;
    @Mock
    SpecialTest1 test1;
    
    @InjectMocks
    TestServiceTest testService;
    
    @BeforeEach()
    void setup () {
        openMocks(this);
        // 手动基于真实Repository创建Spy
        repoSpy = Mockito.spy(realRepo);
        // 把Spy对象注入到依赖它的Service中
        ReflectionTestUtils.setField(testService, "repo", repoSpy);
        
        when(testService.getId()).thenReturn(test1);
    }
    
    @Test
    void test () {
        // 使用repoSpy执行测试逻辑
    }
}

这种方式需要手动处理依赖注入,比较繁琐,所以优先推荐方案1。

额外需要修正的小问题

  1. @InjectMock拼写错误:你代码里写成了@InjectMock,正确注解是@InjectMocks,用来让Mockito自动注入Mock/Spy到目标实例。
  2. 重复的when设置:setup里两次给testService.getId()设置返回值,后一次会覆盖前一次,根据实际需求调整即可。
  3. 多个@InjectMocks:同一个测试类给两个类标记@InjectMocks容易导致依赖混乱,建议一个测试类专注测试一个Service,拆分后逻辑更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:16:50