
当服务类中直接通过 new 创建第三方库对象(如 RequestClient)时,无法直接 mock,需通过工厂模式解耦创建逻辑,再 mock 工厂返回的实例,从而避免真实 API 调用。
当服务类中直接通过 `new` 创建第三方库对象(如 `requestclient`)时,无法直接 mock,需通过工厂模式解耦创建逻辑,再 mock 工厂返回的实例,从而避免真实 api 调用。
在 Spring + Mockito 单元测试中,若业务代码中使用 new RequestClient(...) 实例化第三方工具类,Mockito 无法拦截 new 表达式——这意味着你在测试中 mock(RequestClient.class) 并设置行为,对实际运行时创建的对象完全无效。根本原因在于:mock() 创建的是全新代理对象,而服务方法中 new 出来的仍是原始类的真实实例。
✅ 正确解法:引入工厂层,实现可测试性
核心思路是将对象创建逻辑从服务类中剥离,交由 Spring 管理的组件(如 @Component 工厂)负责,使创建过程可被依赖注入和模拟。
1. 定义可注入的工厂类
@Component
public class RequestClientFactory {
public <t> RequestClient<t> createRequestClient() {
return new RequestClient(new HttpClientFactory());
}
}</t></t>
2. 改造服务类:依赖工厂而非 new
@Service
public class CorrectionService {
@Autowired
private RequestClientFactory requestClientFactory; // ✅ 可注入、可 mock
public BookingDetails getData() {
RequestClient<bookingdetails> requestClient = requestClientFactory.createRequestClient();
ApiResponse<bookingdetails> apiResponse =
requestClient.send(bookingRequest, BookingDetails.class);
return apiResponse.getData();
}
}</bookingdetails></bookingdetails>
3. 编写可生效的单元测试
@ExtendWith(MockitoExtension.class)
class CorrectionServiceTest {
@InjectMocks
private CorrectionService correctionService;
@Mock
private RequestClientFactory requestClientFactory;
@Test
void testGetData_returnsMockedResponse() {
// 1. 创建 RequestClient 的 mock 实例
RequestClient<bookingdetails> mockClient = mock(RequestClient.class);
// 2. 让工厂 mock 返回该实例
when(requestClientFactory.createRequestClient())
.thenReturn(mockClient);
// 3. 配置 mock Client 的 send() 行为
ApiResponse<bookingdetails> mockResponse =
ApiResponse.<bookingdetails>builder()
.data(new BookingDetails("TEST-123"))
.build();
when(mockClient.send(any(), eq(BookingDetails.class)))
.thenReturn(mockResponse);
// 4. 执行并验证
BookingDetails result = correctionService.getData();
assertThat(result).isNotNull();
assertThat(result.getBookingId()).isEqualTo("TEST-123");
}
}</bookingdetails></bookingdetails></bookingdetails>
⚠️ 注意事项与最佳实践
- 避免 new 在业务逻辑中硬编码:这是破坏可测试性的常见根源;应始终优先考虑依赖注入。
- 泛型方法模拟需注意类型擦除:when(...).thenReturn(...) 中 eq(BookingDetails.class) 比 any(Class.class) 更安全,避免因类型匹配失败导致 stub 不生效。
- 工厂本身应轻量无状态:确保 createRequestClient() 不含复杂逻辑或外部依赖,否则需额外 mock 其内部协作对象。
- 若无法修改生产代码(如遗留系统):可考虑 PowerMock(不推荐,已停止维护)或重构为构造函数注入 RequestClient 实例,但工厂方案更符合 Spring 哲学且长期可维护。
通过工厂抽象 + 依赖注入,你不仅解决了当前 mock 问题,更提升了代码的松耦合性、可扩展性和可维护性——这才是面向测试编程(TDD)与 IoC 原则的真正落地。











