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

Java单元测试中调用依赖Bean实例的工具类Mock方案咨询

单元测试Mock静态依赖解决方案

问题核心原因

你当前测试逻辑失效的核心问题有两个:

  • 普通Mockito框架不支持Mock静态方法,你编写的Mockito.when(OAuth2StringUtils.getTokenRedirectURL())会直接执行真实的静态方法逻辑,触发OAuth2CoreConfigProperties.getInstance()调用,拿不到实例会抛出空指针异常
  • 你预先构造的HttpEntity参数和业务代码实际生成的参数不一致,restTemplate.exchange的条件匹配失败,会返回空值

方案1:使用PowerMock扩展Mockito能力(无需修改业务代码)

PowerMock可以支持Mock静态方法、私有方法、单例类等特殊场景,适合不方便改动业务代码的情况:

第一步:引入PowerMock依赖

以JUnit4为例,添加对应依赖:

<!-- PowerMock 核心依赖 -->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

第二步:修改测试类注解

测试类上增加PowerMock相关配置:

@RunWith(PowerMockRunner.class)
@PrepareForTest({OAuth2StringUtils.class, OAuth2CoreConfigProperties.class})
public class WebClientServiceTest {
    // 原有Spy、Mock、InjectMocks注解保持不变
}

第三步:修正Mock逻辑

调整测试用例的Mock代码:

@Test
void should_return_token_given_auth_code_when_get_token() throws Exception {
    // given
    String authCode = "authCode";
    String accessToken = "accessToken";
    TokenDTO tokenDTO = TokenDTO.builder().access_token(accessToken).build();
    ResponseEntity<TokenDTO> response =  ResponseEntity.ok(tokenDTO);

    // Mock静态工具类返回值
    PowerMockito.mockStatic(OAuth2StringUtils.class);
    Mockito.when(OAuth2StringUtils.getTokenRedirectURL()).thenReturn("token_redirect_url");

    // 调整restTemplate匹配规则,用any匹配HttpEntity,避免参数不一致匹配失败
    Mockito.when(restTemplate.exchange(
            Mockito.eq(config.getTokenEndpoint()),
            Mockito.eq(HttpMethod.POST),
            Mockito.any(HttpEntity.class),
            Mockito.eq(TokenDTO.class)
    )).thenReturn(response);

    // then
    TokenDTO result = webClientService.getToken(authCode);
    assertEquals(accessToken, result.getAccess_token());
}

方案2:重构业务代码解耦静态依赖(更推荐)

硬编码的静态方法+单例调用不符合Spring依赖注入的设计规范,会降低代码可扩展性和可测试性,建议重构:

重构步骤:

  1. 移除OAuth2StringUtils的静态方法,将其改为普通Spring Bean,通过依赖注入获取OAuth2CoreConfigProperties实例:
@Component
public class OAuth2StringUtils {
    @Autowired
    private OAuth2CoreConfigProperties configProperties;

    public String getTokenRedirectURL() {
        return configProperties.getTokenRedirectUrl();
    }
}
  1. 在WebClientServiceImpl中注入OAuth2StringUtils实例,替换原有静态调用:
@Service
public class WebClientServiceImpl implements WebClientService {
    @Autowired
    private OAuth2StringUtils oAuth2StringUtils;

    @Override
    public TokenDTO getToken(String authCode) {
        HttpEntity<MultiValueMap<String, String>> entity = generateCommonHttpEntity();
        entity.getBody().add("grant_type", OAuth2Constants.GRANT_TYPE_AUTHORIZATION_CODE);
        // 替换为实例方法调用
        entity.getBody().add("redirect_uri", oAuth2StringUtils.getTokenRedirectURL());
        entity.getBody().add("code",authCode);
        return getTokenByPOST(entity);
    }
}

重构后的测试逻辑

不需要引入PowerMock,直接用普通Mockito即可完成Mock:

// JUnit5用@ExtendWith(MockitoExtension.class),JUnit4用@RunWith(MockitoJUnitRunner.class)
@ExtendWith(MockitoExtension.class)
public class WebClientServiceTest {
    @Spy
    @InjectMocks
    WebClientServiceImpl webClientService;

    @Mock
    OAuth2CoreConfigProperties config;

    @Mock
    RestTemplate restTemplate;

    @Mock
    OAuth2StringUtils oAuth2StringUtils; // 直接Mock实例

    @Test
    void should_return_token_given_auth_code_when_get_token() throws Exception {
        String authCode = "authCode";
        String accessToken = "accessToken";
        TokenDTO tokenDTO = TokenDTO.builder().access_token(accessToken).build();
        ResponseEntity<TokenDTO> response =  ResponseEntity.ok(tokenDTO);

        // Mock普通实例方法即可
        Mockito.when(oAuth2StringUtils.getTokenRedirectURL()).thenReturn("token_redirect_url");
        Mockito.when(restTemplate.exchange(
                Mockito.eq(config.getTokenEndpoint()),
                Mockito.eq(HttpMethod.POST),
                Mockito.any(HttpEntity.class),
                Mockito.eq(TokenDTO.class)
        )).thenReturn(response);

        TokenDTO result = webClientService.getToken(authCode);
        assertEquals(accessToken, result.getAccess_token());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 09:27:04