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

JUnit 5中嵌套映射测试出现NullPointerException问题排查

解决Mapper空指针异常问题

报错信息

java.lang.NullPointerException: Cannot invoke "com.fincons.mapper.EmployeeMapper.mapEmployeeToEmployeeDtoWithoutObjects(com.fincons.entity.Employee)" because "this.employeeMapper" is null

at com.fincons.mapper.DepartmentMapper.lambda$mapDepartmentToDepartmentDto$0(DepartmentMapper.java:26)

问题根源

DepartmentMapper中依赖了EmployeeMapper,但测试代码仅创建了DepartmentMapper的Spy实例,未将EmployeeMapper注入其中,导致调用mapDepartmentToDepartmentDto方法时this.employeeMapper为null。同时测试类中存在冗余操作:既用@InjectMocks注解,又在@BeforeEach里手动实例化departmentService,造成依赖注入混乱。

解决方案

方案1:给DepartmentMapper添加构造注入

先修改DepartmentMapper,通过构造方法接收EmployeeMapper:

public class DepartmentMapper {
    private final EmployeeMapper employeeMapper;

    // 构造方法注入依赖
    public DepartmentMapper(EmployeeMapper employeeMapper) {
        this.employeeMapper = employeeMapper;
    }

    public DepartmentDTO mapDepartmentToDepartmentDto(Department department) {
        if (department.getEmployees() != null) {
            List<EmployeeDTO> employeeDTOList = new ArrayList<>();

            department.getEmployees().forEach(employee ->
                employeeDTOList.add(employeeMapper.mapEmployeeToEmployeeDtoWithoutObjects(employee))
            );
            return new DepartmentDTO(
                    department.getId(),
                    department.getName(),
                    department.getAddress(),
                    department.getCity(),
                    employeeDTOList);
        }
        // 补充空列表场景的返回逻辑
        return new DepartmentDTO(department.getId(), department.getName(), department.getAddress(), department.getCity(), null);
    }
}

调整测试类,移除冗余的手动初始化代码,让Mockito自动处理依赖:

@ExtendWith(MockitoExtension.class)
public class DepartmentServiceTest {

    @InjectMocks
    private DepartmentServiceImpl departmentService;

    @Mock
    private DepartmentRepository departmentRepository;

    @Spy
    private EmployeeMapper employeeMapper;

    // 构造DepartmentMapper Spy时传入依赖的EmployeeMapper
    @Spy
    private DepartmentMapper departmentMapper = new DepartmentMapper(employeeMapper);

    @BeforeEach
    void init() {
        // 移除以下冗余代码:
        // departmentRepository = mock(DepartmentRepository.class);
        // departmentService = new DepartmentServiceImpl(departmentRepository, departmentMapper, employeeMapper);
    }

    @Test
    void testGetDepartmentByIdExist() {
        long departmentId = 3L;
        Department existingDepartment = new Department(3L, "name", "address", "city");
        Position position = new Position(3L, "Web Developer", 1500.0);

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate date1 = LocalDate.parse("1942-03-10", formatter);
        LocalDate date2 = LocalDate.parse("1942-03-10", formatter);
        LocalDate date3 = LocalDate.parse("1942-03-10", formatter);
        Employee employee = new Employee(5L, "Carlo", "Verdone", "carlo.verdone@gmail.com" , "Male", date1, date2, date3 , existingDepartment, position);
        List<Employee> newListEmployee = new ArrayList<>();
        newListEmployee.add(employee);
        existingDepartment.setEmployees(newListEmployee);

        // 修正:JPA的findById默认返回Optional,匹配方法签名
        when(departmentRepository.findById(departmentId)).thenReturn(Optional.of(existingDepartment));

        ResponseEntity<Object> response = departmentService.getDepartmentById(departmentId);

        assertTrue(response.getStatusCode().is2xxSuccessful());
    }
}

方案2:将Mapper声明为Spring组件,用SpyBean管理

如果Mapper是Spring管理的组件(添加@Component注解),可借助@SpyBean让Spring容器处理依赖注入:

修改DepartmentMapper:

@Component
public class DepartmentMapper {
    @Autowired
    private EmployeeMapper employeeMapper;

    // 原map方法逻辑不变
}

修改测试类:

@SpringBootTest
@ExtendWith(MockitoExtension.class)
public class DepartmentServiceTest {

    @InjectMocks
    private DepartmentServiceImpl departmentService;

    @Mock
    private DepartmentRepository departmentRepository;

    @SpyBean
    private DepartmentMapper departmentMapper;

    @SpyBean
    private EmployeeMapper employeeMapper;

    @BeforeEach
    void init() {
        // 移除所有手动初始化代码
    }

    // 测试方法同方案1
}

核心逻辑总结

两种方案的核心都是确保DepartmentMapper实例能获取到有效的EmployeeMapper实例:要么通过构造方法显式传递依赖,要么借助Spring容器完成注入,避免依赖对象为null。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 08:00:33