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

Mockito单元测试中when(..)服务方法存根不生效问题排查

为什么Mockito存根不生效?真实服务被调用的原因及解决办法

核心原因:Spring上下文覆盖了Mock对象

你使用了@SpringBootTest注解,该注解会启动完整的Spring应用上下文。此时Ar11Application作为标注@SpringBootApplication的类,会被Spring自动实例化,其内部@Autowired的ar11ApplicationService会被注入真实的Ar11ApplicationServiceImpl实例。你通过@InjectMocks创建的测试用Ar11Application实例,会被Spring容器中的实例覆盖,导致run方法调用的是真实服务而非mock对象。

额外问题:JUnit注解冲突

测试类混合使用了@RunWith(MockitoJUnitRunner.class)(JUnit4运行器)和@ExtendWith(MockitoExtension.class)(JUnit5扩展),属于冗余配置且可能引发冲突。由于你使用了JUnit5的@BeforeEach和org.junit.jupiter.api.Assertions,应当移除@RunWith注解。


方案一:使用@SpringBootTest + @MockBean(推荐,保留Spring上下文)

这种方式让Spring将mock服务替换容器中的真实实现,同时保留Spring自动配置(如@Value属性注入):

@SpringBootTest
@ExtendWith(MockitoExtension.class)
class Ar11ApplicationTest {

    // 用@MockBean替代@Mock,Spring会将该mock实例注入容器
    @MockBean
    Ar11ApplicationService ar11ApplicationService;
    
    @MockBean
    EmailService emailService;

    // 直接@Autowired获取Spring管理的Ar11Application实例,自动注入mock服务
    @Autowired
    Ar11Application ar11Application;
    
    @BeforeEach
    void setUp() throws Exception {
        Mockito.when(ar11ApplicationService.processEligibleRecords(
                Mockito.anyString(), Mockito.anyString(), 
                Mockito.anyString(), Mockito.anyString(), 
                Mockito.anyString(), Mockito.anyString(), 
                Mockito.anyString(), Mockito.anyInt(), Mockito.anyString()))
              .thenReturn(new Ar11ResultBean());
    }

    @Test
    void main() throws Exception {
        org.junit.jupiter.api.Assertions.assertDoesNotThrow(
            () -> ar11Application.run(new String[] {})
        );
    }
}

方案二:纯Mockito测试(不加载Spring上下文)

若无需Spring自动配置,仅测试Ar11Application的逻辑,可移除@SpringBootTest,手动设置@Value属性值:

@ExtendWith(MockitoExtension.class)
class Ar11ApplicationTest {

    @Mock
    Ar11ApplicationService ar11ApplicationService;
    
    @Mock
    EmailService emailService;
    
    @InjectMocks
    Ar11Application ar11Application;
    
    @BeforeEach
    void setUp() throws Exception {
        // 手动设置@Value注入的属性值(纯Mockito不处理@Value)
        ReflectionTestUtils.setField(ar11Application, "eligibleFilename", "test-eligible.txt");
        ReflectionTestUtils.setField(ar11Application, "newFilenamePrefix", "test-new-");
        ReflectionTestUtils.setField(ar11Application, "sftpUserName", "test-user");
        ReflectionTestUtils.setField(ar11Application, "sftpPrivateKeyPath", "/test/path");
        ReflectionTestUtils.setField(ar11Application, "sftpPrivateKeyPassphrase", "test-pass");
        ReflectionTestUtils.setField(ar11Application, "sftpHost", "test-host");
        ReflectionTestUtils.setField(ar11Application, "sftpPort", 22);
        ReflectionTestUtils.setField(ar11Application, "sourceDir", "/test/source");
        ReflectionTestUtils.setField(ar11Application, "remoteDir", "/test/remote");

        // 存根设置不变
        Mockito.when(ar11ApplicationService.processEligibleRecords(
                Mockito.anyString(), Mockito.anyString(), 
                Mockito.anyString(), Mockito.anyString(), 
                Mockito.anyString(), Mockito.anyString(), 
                Mockito.anyString(), Mockito.anyInt(), Mockito.anyString()))
              .thenReturn(new Ar11ResultBean());
    }

    @Test
    void main() throws Exception {
        org.junit.jupiter.api.Assertions.assertDoesNotThrow(
            () -> ar11Application.run(new String[] {})
        );
    }
}

内容的提问来源于stack exchange,提问作者gene b.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:35:18