如何使用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
相关产品推荐
相关产品推荐

