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

如何使用Mockito Mock待测试方法内部调用的私有方法

Mockito单元测试实现方案

适用场景说明

你要mock的buildGetSubtenantsURL、getSubtenants、getSubtenantName等方法与addNewMap属于同一类的场景,优先用Mockito Spy实现部分方法mock,Spy会保留类的原有真实逻辑,仅对指定方法返回自定义模拟值。如果待mock的是私有方法,需要配合PowerMock实现。

1. 依赖配置(Maven示例)

Mockito核心依赖:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>

需要mock私有方法时额外添加PowerMock依赖:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

2. 非私有方法Mock实现(方法为public/protected/包可见)

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class MapServiceTest {
    // 用Spy创建被测类实例,保留真实逻辑,仅mock指定方法
    @Spy
    private MapService mapService; // MapService为addNewMap所在的类名,根据实际修改

    @Test
    public void testAddNewMap_SubtenantExists_Success() {
        // 1. 构造测试入参和假数据
        MapDTO testMapDTO = new MapDTO();
        testMapDTO.setSubtenantName("test_subtenant");
        String mockSubtenantsURL = "http://test-url/subtenants";
        String mockSubtenantsResponse = "{\"content\": [{\"name\": \"test_subtenant\", \"id\": \"123\"}]}";
        JSONObject mockSubTenant = new JSONObject();
        mockSubTenant.put("name", "test_subtenant");
        UriComponents mockAssetsURL = UriComponentsBuilder.fromHttpUrl("http://test-url/assets").build();
        String mockAssetsResponse = "{\"assetId\": \"asset_456\"}";

        // 2. Mock内部调用的方法
        doReturn(mockSubtenantsURL).when(mapService).buildGetSubtenantsURL(isNull());
        doReturn(mockSubtenantsResponse).when(mapService).getSubtenants(eq(mockSubtenantsURL), anyString());
        doReturn(mockSubTenant).when(mapService).getSubtenantName(eq(mockSubtenantsResponse));
        doNothing().when(mapService).checkForMultiplePagesSubtenants(eq(mockSubtenantsResponse), anyMap(), anyString());
        doReturn(mockAssetsURL).when(mapService).buildGetAssetsURL(anyString(), eq("123"));
        doReturn(mockAssetsResponse).when(mapService).getAssets(eq(mockAssetsURL), anyString());
        doReturn("asset_456").when(mapService).getAssetId(eq(mockAssetsResponse));

        // 3. 执行被测方法
        mapService.addNewMap(testMapDTO);

        // 4. 验证方法调用符合预期
        verify(mapService, times(1)).buildGetSubtenantsURL(isNull());
        verify(mapService, times(1)).getSubtenants(eq(mockSubtenantsURL), anyString());
    }

    @Test(expected = DataNotFoundException.class)
    public void testAddNewMap_SubtenantNotExists_ThrowException() {
        // 构造入参:subtenant名称不存在于模拟数据中
        MapDTO testMapDTO = new MapDTO();
        testMapDTO.setSubtenantName("not_exist_subtenant");
        String mockSubtenantsURL = "http://test-url/subtenants";
        String mockSubtenantsResponse = "{\"content\": []}";
        JSONObject mockSubTenant = new JSONObject();

        doReturn(mockSubtenantsURL).when(mapService).buildGetSubtenantsURL(isNull());
        doReturn(mockSubtenantsResponse).when(mapService).getSubtenants(anyString(), anyString());
        doReturn(mockSubTenant).when(mapService).getSubtenantName(anyString());
        doNothing().when(mapService).checkForMultiplePagesSubtenants(anyString(), anyMap(), anyString());

        // 执行方法会抛出预期异常
        mapService.addNewMap(testMapDTO);
    }
}

3. 私有方法Mock实现(配合PowerMock)

Mockito原生不支持mock私有方法,需要配合PowerMock实现:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.ArgumentMatchers.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MapService.class) // 指定要处理的包含私有方法的类
public class MapServiceTest {
    private MapService mapService = PowerMockito.spy(new MapService());

    @Test
    public void testAddNewMap() throws Exception {
        // 构造假数据逻辑和上文一致
        String mockSubtenantsURL = "http://test-url/subtenants";
        // Mock私有方法,指定方法名和参数匹配规则
        PowerMockito.doReturn(mockSubtenantsURL).when(mapService, "buildGetSubtenantsURL", isNull());
        // 其余mock逻辑、执行、校验逻辑和上文一致
    }
}

4. 测试注意点

  • 要覆盖两个分支:分别测试subtenant存在、不存在的场景,覆盖if/else逻辑
  • 参数匹配规则:如果需要校验方法入参是否正确,用eq()匹配指定值,any()匹配任意值,不要混合使用字面量和匹配器
  • 不要mock被测方法本身:addNewMap要调用真实逻辑,仅mock它内部依赖的外部调用相关方法即可
  • 可以用verify方法验证mock的方法调用次数、入参是否符合预期,确保逻辑执行正确

内容的提问来源于stack exchange,提问作者Shivani Shankar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 05:54:05