Java中如何Mock被测试方法依赖的受保护类实例?
测试构造方法为protected的POJO类的实用技巧
问题背景
有一个公开的POJO类RequestInfo,构造方法为protected,Setter方法为protected或private,仅Getter方法对外公开:
public class RequestInfo { private String user; private String date; private String action; protected RequestInfo(String user, String date, String action) { this.user = user; this.date = date; this.action = action; } public String getUser(){ return user; } // 省略其他Getter及私有/protected Setter private void setUser(String user){ this.user = user; } }
需要测试接收该POJO实例的业务方法getReport:
public String getReport(RequestInfo requestInfo){ String requestUser = requestInfo.getUser(); String requestDate = requestInfo.getDate(); String requestAction = requestInfo.getAction(); // 业务逻辑处理 }
遇到的核心问题:
- 无法直接实例化
RequestInfo传入测试值,构造方法为protected; - 直接用
Mockito.mock(RequestInfo.class)生成的实例,所有Getter返回null,导致被测方法执行异常; - 不想修改原有代码设计(如将构造方法改为public)。
解决方案
1. 反射调用protected构造方法
通过Java反射机制绕过访问权限,直接调用protected构造创建实例:
@Test public void getReportTest() throws Exception { // 获取目标构造方法 Constructor<RequestInfo> constructor = RequestInfo.class.getDeclaredConstructor(String.class, String.class, String.class); // 开启访问权限 constructor.setAccessible(true); // 创建带测试数据的实例 RequestInfo requestInfo = constructor.newInstance("john", "12DEC22", "addUser"); String report = ReportsManager.getReport(requestInfo); assertEquals("预期的报告内容", report); }
2. Mock并Stub Getter方法
不需要真实实例时,直接Mock对象并为所有用到的Getter方法预设返回值:
@Test public void getReportTest() { RequestInfo mockRequest = Mockito.mock(RequestInfo.class); // 为每个Getter设置测试返回值 Mockito.when(mockRequest.getUser()).thenReturn("john"); Mockito.when(mockRequest.getDate()).thenReturn("12DEC22"); Mockito.when(mockRequest.getAction()).thenReturn("addUser"); String report = ReportsManager.getReport(mockRequest); assertEquals("预期的报告内容", report); }
3. 创建测试专用子类
在测试包下定义RequestInfo的子类,利用子类可访问父类protected构造的特性:
// 测试包内的子类,仅用于测试 public class TestableRequestInfo extends RequestInfo { public TestableRequestInfo(String user, String date, String action) { super(user, date, action); } } // 测试用例 @Test public void getReportTest() { RequestInfo requestInfo = new TestableRequestInfo("john", "12DEC22", "addUser"); String report = ReportsManager.getReport(requestInfo); assertEquals("预期的报告内容", report); }
4. 使用PowerMock(允许额外依赖时)
若项目允许引入PowerMock依赖,可直接创建实例或修改私有字段:
@RunWith(PowerMockRunner.class) @PrepareForTest(RequestInfo.class) public class ReportsManagerTest { @Test public void getReportTest() throws Exception { // 直接创建带参数的实例 RequestInfo requestInfo = PowerMockito.newInstance(RequestInfo.class, "john", "12DEC22", "addUser"); String report = ReportsManager.getReport(requestInfo); assertEquals("预期的报告内容", report); } }
内容的提问来源于stack exchange,提问作者NoobCoder
相关产品推荐
相关产品推荐

