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

Spring单元测试用Mockito获取实体嵌套对象报空指针如何解决

Spring Service层Mockito单元测试空指针问题修复

问题复现

待测试的Service层业务逻辑包含多层实体关联调用,代码片段如下:

VirtualDatacenterModel vdc = vdcRepository.findById(vmDTO.getVdc()).orElseThrow(() -> new ClientException("Invalid VDC id"));
DataCenterModel dc = vdc.getDatacenter();
String vmName = vdc.getTenant().getName() + "_[" + vmDTO.getName() + "]";

使用Mockito编写的测试代码运行时抛出NullPointerException,原测试代码:

CreateVmDTO vmDTO = Mockito.mock(CreateVmDTO.class);
VmModel vm = Mockito.mock(VmModel.class);
VirtualDatacenterModel vdc = Mockito.mock(VirtualDatacenterModel.class, Mockito.RETURNS_DEEP_STUBS);
TenantModel tenant = Mockito.mock(TenantModel.class);
Mockito.when(vmRepository.save(vm)).thenReturn(new VmModel());
Mockito.when(vdcRepository.findById(vmDTO.getVdc())).thenReturn(Optional.of(new VirtualDatacenterModel()));
Mockito.doReturn(tenant).when(vdc).getTenant();
Mockito.when(vdc.getTenant().getName()).thenReturn("Olivia");

VmModel vmReturn = vmService.createVM(vmDTO);

错误原因

空指针由核心Mock配置错误导致:

  1. 手动Mock并配置了关联关系的vdc对象没有被业务代码实际调用:vdcRepository.findById()的存根返回的是新new出来的VirtualDatacenterModel真实实例,这个实例没有设置任何关联属性,getTenant()默认返回null,后续调用getName()直接触发空指针。
  2. vmRepository.save()的存根参数匹配错误:存根指定只有传入手动Mock的vm实例时才会触发返回值,但业务逻辑中传给save方法的是Service内部构造的VmModel对象,和定义的Mock实例不相等,该存根根本不会生效。
  3. 给vdc设置RETURNS_DEEP_STUBS后又手动配置getTenant()的返回值,配置逻辑本身存在冲突,属于冗余写法。

正确写法

核心原则是:Repository层存根返回的对象,必须是提前配置好所有关联属性的实例(不管是Mock对象还是手动set属性的真实实体对象)。修正后的测试代码:

// 配置测试入参的存根,明确业务逻辑会用到的字段返回值
CreateVmDTO vmDTO = Mockito.mock(CreateVmDTO.class);
Mockito.when(vmDTO.getVdc()).thenReturn(1L); // 替换为实际使用的VDC ID类型,比如Long/UUID
Mockito.when(vmDTO.getName()).thenReturn("test-biz-vm");

// 逐层构造关联实体的Mock,配置需要用到的返回值
TenantModel tenant = Mockito.mock(TenantModel.class);
Mockito.when(tenant.getName()).thenReturn("Olivia");

DataCenterModel dc = Mockito.mock(DataCenterModel.class);

VirtualDatacenterModel vdc = Mockito.mock(VirtualDatacenterModel.class);
Mockito.when(vdc.getTenant()).thenReturn(tenant);
Mockito.when(vdc.getDatacenter()).thenReturn(dc);

// 配置Repository层存根,返回上面配置好关联关系的vdc对象,不要new新的实例
Mockito.when(vdcRepository.findById(eq(vmDTO.getVdc()))).thenReturn(Optional.of(vdc));
// save方法用参数匹配器any()接收任意VmModel实例,直接返回入参即可,不需要绑定特定Mock对象
Mockito.when(vmRepository.save(any(VmModel.class))).thenAnswer(invocation -> invocation.getArgument(0));

// 执行业务方法
VmModel vmReturn = vmService.createVM(vmDTO);

// 后续补充业务断言即可

优化建议:如果实体类逻辑简单,不需要全部用Mock,可以直接new真实实体,通过setter方法设置关联属性,这种写法更贴近真实运行场景,能避免很多Mock配置和实际逻辑脱节的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 12:31:42