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

JUnit测试中@Autowired与@InjectMocks切换导致测试结果互斥问题

问题原因分析

1. 使用@Autowired注入时测试2失败的原因

  • @SpringBootTest会启动完整的Spring应用上下文,@Autowired注入的是真实的NotificationRepository实例,它依赖的SendNotificationDao也是Spring容器中初始化的真实DAO对象,而非你定义的@Mock实例。
  • 测试2中你对sendNotificationDao.fetchAllTokens()的Mock配置完全无效,实际调用的是真实DAO的数据库查询方法,返回的是真实数据,所以tokens.size() > 0为true,导致assertFalse断言失败。

2. 使用@InjectMocks注入时测试1失败的原因

  • @InjectMocks是Mockito的注解,它会创建一个全新的NotificationRepository实例,并将你定义的@Mock SendNotificationDao注入进去,这个实例和Spring容器中的真实实例毫无关联。
  • 测试1中调用getAllTokens()时,使用的是Mock的SendNotificationDao,而你没有为这个Mock配置任何返回值,Mockito默认会返回空集合,所以tokens.size() > 0为false,导致assertTrue断言失败。
解决方案

你需要明确两种测试的定位,分开处理:

方案一:拆分集成测试与单元测试

集成测试(验证真实数据库交互)

专门写一个集成测试类,只保留测试1,使用@Autowired注入真实组件,依赖真实数据库:

@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class NotificationRepositoryIntegrationTest {

    @Autowired
    private NotificationRepository notificationRepository;

    @Test
    @Order(1)
    public void getAllTokensTest() {
        String platformId = "push-notifications-27933";
        List<String> tokens = notificationRepository.getAllTokens(platformId);
        assertNotNull(tokens);
        assertTrue(tokens.size() > 0);
    }
}

单元测试(验证逻辑,Mock依赖)

写一个单元测试类,只保留测试2,使用Mockito的@InjectMocks和@Mock,不需要启动Spring上下文(去掉@SpringBootTest):

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class NotificationRepositoryUnitTest {

    @InjectMocks
    private NotificationRepository notificationRepository;
    
    @Mock
    private SendNotificationDao sendNotificationDao;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    @Order(2)
    public void tokenExistsTest() {
        String platformId = "some String";
        when(sendNotificationDao.fetchAllTokens(platformId)).thenReturn(new ArrayList<>());
        List<String> tokens = notificationRepository.getAllTokens(platformId);
        assertFalse(tokens.size() > 0);
    }
}

方案二:在同一个测试类中使用@MockBean(适配两种场景)

如果要在同一个类中保留两个测试,可以用Spring Boot的@MockBean替代@Mock,它会替换Spring上下文中的SendNotificationDao为Mock实例:

@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class NotificationRepositoryTest {

    @Autowired
    private NotificationRepository notificationRepository;
    
    @MockBean // 替换Spring上下文里的真实DAO为Mock
    private SendNotificationDao sendNotificationDao;

    @Test
    @Order(1)
    public void getAllTokensTest() {
        // 为测试1配置Mock返回模拟的真实数据结果
        String platformId = "push-notifications-27933";
        List<String> mockTokens = Arrays.asList("token1", "token2");
        when(sendNotificationDao.fetchAllTokens(platformId)).thenReturn(mockTokens);
        
        List<String> tokens = notificationRepository.getAllTokens(platformId);
        assertNotNull(tokens);
        assertTrue(tokens.size() > 0);
    }

    @Test
    @Order(2)
    public void tokenExistsTest() {
        String platformId = "some String";
        when(sendNotificationDao.fetchAllTokens(platformId)).thenReturn(new ArrayList<>());
        List<String> tokens = notificationRepository.getAllTokens(platformId);
        assertFalse(tokens.size() > 0);
    }
}

注意:这种方式下测试1的结果依赖Mock配置,而非真实数据库,如果需要验证真实数据库交互,还是建议用方案一的集成测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 11:38:14