本文介绍一种符合 mockito 原则、无需 powermockito 或反射的轻量级方案:通过受保护的构造函数注入依赖,实现对类内部新建对象(如 client)的行为模拟,兼顾可测试性与代码整洁性。
本文介绍一种符合 mockito 原则、无需 powermockito 或反射的轻量级方案:通过受保护的构造函数注入依赖,实现对类内部新建对象(如 client)的行为模拟,兼顾可测试性与代码整洁性。
在单元测试中,当被测类(如 MyClass)自行实例化其依赖(例如通过 setupClient() 创建 Client 对象),而该依赖并未通过构造函数或 setter 注入时,直接用 Mockito 模拟其行为会受限——因为 Mockito 无法拦截私有方法调用或控制内部对象生命周期。但强行使用 PowerMockito 或修改类结构(如添加 @PrepareForTest)不仅增加复杂度,还违背“测试友好设计”的初衷。
推荐实践:引入受保护的构造函数(Protected Constructor)
这不是破坏封装,而是为测试提供一条合法、低侵入的扩展路径。核心思想是:保留原有无参构造函数供生产环境使用,同时新增一个带依赖参数的 protected 构造函数,仅用于测试场景:
public class MyClass { private final Client someClient; // 生产环境使用的默认构造函数 public MyClass() { this.someClient = setupClient(); } // 仅用于测试的受保护构造函数(不暴露给外部模块) protected MyClass(Client client) { this.someClient = Objects.requireNonNull(client, "Client must not be null"); } private Client setupClient() { // 实际初始化逻辑,如 new OkHttpClient() 或配置连接池等 return new DefaultClient(); } public void methodIWantToTest(String requestString) { this.someClient.makeApiCall(requestString); // 其他业务逻辑 }}
在测试类中,直接使用该受保护构造函数传入 Mock 对象:
public class MyClassTest { private MyClass SUT; private Client mockClient; @BeforeEach void setUp() { mockClient = Mockito.mock(Client.class); SUT = new MyClass(mockClient); // ✅ 使用受保护构造函数 } @Test void testMethodIWantToTest() { // 配置模拟行为 Mockito.when(mockClient.makeApiCall(Mockito.anyString())) .thenReturn(Response.success("mocked response")); // 执行被测方法 SUT.methodIWantToTest("GET /api/users"); // 验证交互 Mockito.verify(mockClient).makeApiCall("GET /api/users"); Mockito.verifyNoMoreInteractions(mockClient); }}
✅ 优势说明:
⚠️ 注意事项:
总结:可测试性不是测试框架的义务,而是设计的责任。通过微小的构造函数重构,即可在零反射、零第三方工具的前提下,让私有依赖变得“可替换、可验证、可预测”,这才是 Mockito 倡导的正向测试实践。
《幻兽帕鲁》抓保护区警卫用处解析-劳工功能详细说明
pubg地铁逃生国际服正版极速无广下载-pubg地铁逃生(PUBG MOBILE)国际服下载
遗忘之海第三章全流程任务攻略 遗忘之海第三章主线与隐藏任务详细说明
《幻兽帕鲁》安全进入野生动物保护区攻略-飞行帕鲁与装备选择详细说明
梦战剑之海军团要塞 梦战剑之海军团要塞核心功能与实战用途详细说明
《幻兽帕鲁》保护区BOSS打法详细说明-野生动物保护区挑战攻略