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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:01:17