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

如何用Mockito模拟第三方库?Java服务RequestClient模拟问题

问题原因

你当前的模拟代码无效,核心原因是:测试中mock的RequestClient实例,和getData()方法内部new出来的实例完全是两个独立对象,mock的配置不会作用到真实代码创建的对象上,所以仍然会触发真实的API调用。

解决方案

方案1:重构代码(推荐,符合Spring设计理念)

通过抽取RequestClient的创建逻辑为可注入的工厂类,让代码具备可测试性:

步骤1:定义RequestClient工厂接口与实现

// 工厂接口,统一管理RequestClient的创建
interface RequestClientFactory {
    <T> RequestClient<T> createRequestClient();
}

// 生产环境的工厂实现
@Component
class DefaultRequestClientFactory implements RequestClientFactory {
    @Override
    public <T> RequestClient<T> createRequestClient() {
        return new RequestClient<>(new HttpClientFactory());
    }
}

步骤2:修改CorrectionService,注入工厂

@Service
class CorrectionService {

    private final RequestClientFactory requestClientFactory;

    // 构造注入(Spring官方推荐的依赖注入方式)
    public CorrectionService(RequestClientFactory requestClientFactory) {
        this.requestClientFactory = requestClientFactory;
    }

    BookingDetails getData() {
        // 通过工厂创建RequestClient,而非直接new
        RequestClient<BookingDetails> requestClient = requestClientFactory.createRequestClient();
        ApiResponse<BookingDetails> apiResponse =
            requestClient.send(bookingRequest, BookingDetails.class);
        return apiResponse.getData();
    }
}

步骤3:编写测试代码

现在可以mock工厂类,让它返回预设的RequestClient mock实例:

@ExtendWith(MockitoExtension.class) // JUnit 5注解,JUnit 4用@RunWith(MockitoJUnitRunner.class)
class CorrectionServiceTest {

    @InjectMocks
    CorrectionService correctionService;

    @Mock
    RequestClientFactory requestClientFactory;

    @Test
    void getData_shouldReturnMockedData() {
        // 1. 创建并配置RequestClient的mock实例
        RequestClient<BookingDetails> mockRequestClient = mock(RequestClient.class);
        ApiResponse<BookingDetails> mockApiResponse = ApiResponse.<BookingDetails>builder().build();
        when(mockRequestClient.send(any(), eq(BookingDetails.class)))
            .thenReturn(mockApiResponse);

        // 2. 让工厂返回mock的RequestClient
        when(requestClientFactory.createRequestClient()).thenReturn(mockRequestClient);

        // 3. 执行测试方法
        BookingDetails result = correctionService.getData();

        // 4. 验证逻辑(可选,确保send方法被正确调用)
        verify(mockRequestClient).send(any(), eq(BookingDetails.class));
    }
}

方案2:使用PowerMock(临时方案,不推荐)

如果暂时无法重构代码,可以用PowerMock拦截new操作,让它返回mock实例,但这种方式会让测试和代码实现强绑定,后续维护成本高:

测试代码示例

@RunWith(PowerMockRunner.class) // JUnit 4注解,JUnit 5需用PowerMockExtension
@PrepareForTest({CorrectionService.class, RequestClient.class})
class CorrectionServiceTest {

    @InjectMocks
    CorrectionService correctionService;

    @Test
    void getData_shouldReturnMockedData() throws Exception {
        // 1. 创建并配置RequestClient的mock实例
        RequestClient<BookingDetails> mockRequestClient = mock(RequestClient.class);
        ApiResponse<BookingDetails> mockApiResponse = ApiResponse.<BookingDetails>builder().build();
        when(mockRequestClient.send(any(), eq(BookingDetails.class)))
            .thenReturn(mockApiResponse);

        // 2. 拦截RequestClient的new操作,返回mock实例
        PowerMockito.whenNew(RequestClient.class)
            .withArgumentMatchers(any(HttpClientFactory.class))
            .thenReturn(mockRequestClient);

        // 3. 执行测试
        BookingDetails result = correctionService.getData();

        // 4. 验证调用
        verify(mockRequestClient).send(any(), eq(BookingDetails.class));
    }
}

注意:使用PowerMock需要额外引入powermock-module-junit4和powermock-api-mockito2等依赖,且该方式会绕过Java的正常类加载机制,可能引发意外问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 06:15:07