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

Spring Boot Service层JUnit测试:Dozer对象转换失败求助

问题分析与解决建议

你的问题核心在于Mock了DozerBeanMapper但未正确Stub其map方法,导致Service层调用对象转换时,Mock的Dozer默认返回null,最终生成的EmployeeDTO列表全是null,既无法得到预期的DTO对象,还会导致断言失败(甚至可能触发空指针异常)。另外你的测试断言里写了assertEquals(4, ...),但Mock的Dao只返回了2个Employee,这明显是个笔误,也需要修正。

下面是具体的解决步骤和修改后的代码:

步骤1:准备测试用的EmployeeDTO实例

先创建与Mock返回的Employee对应的DTO对象,用于Stub Doser的转换逻辑。

步骤2:正确Stub Dozer的map方法

使用Mockito的when(...).thenReturn(...),指定当Dozer调用map方法转换某个Employee时,返回对应的EmployeeDTO。如果想通用处理所有Employee的转换,可以用any(Employee.class)来匹配参数。

步骤3:修正断言逻辑

Mock的Dao返回2个Employee,所以Service返回的DTO列表size应该是2,不是4。


修改后的完整测试代码

@SpringBootTest(classes = { EmployeeSearchService.class, EmployeeDao.class }) 
public class EmployeeSearchServiceTest { 
    @Mock 
    EmployeeDao employeeDao; 
    @InjectMocks 
    private EmployeeSearchService employeeSearchService ; 
    @Mock 
    private DozerBeanMapper dozerBeanMapper; 

    @Before 
    public void setUp() throws Exception { 
        MockitoAnnotations.initMocks(this); 
    } 

    @DisplayName("fetchEmployees with valid data") 
    @Test 
    public void testfetchEmployeesWithValidData() throws IOException { 
        // 1. 准备测试用的Employee和对应的EmployeeDTO
        Employee emp1 = new Employee(1L, "James", "Java", "Manager", "Google");
        Employee emp2 = new Employee(2L, "Richard", "C++", "Manager", "Microsoft");
        EmployeeDTO dto1 = new EmployeeDTO(1L, "James", "Java", "Manager", "Google");
        EmployeeDTO dto2 = new EmployeeDTO(2L, "Richard", "C++", "Manager", "Microsoft");

        // 2. Mock Dao的返回值
        when(employeeDao.fetchEmployees()).thenReturn(List.of(emp1, emp2));

        // 3. Stub Dozer的map方法:匹配任意Employee,返回对应的DTO;或者精确匹配每个Employee
        // 方式一:通用匹配(适合批量转换场景)
        when(dozerBeanMapper.map(Mockito.any(Employee.class), Mockito.eq(EmployeeDTO.class)))
            .thenAnswer(invocation -> {
                Employee source = invocation.getArgument(0);
                // 这里可以手动模拟Dozer的转换逻辑,或者直接返回预定义的DTO
                if (source.getId().equals(1L)) return dto1;
                if (source.getId().equals(2L)) return dto2;
                return null;
            });

        // 方式二:精确匹配每个Employee(更严谨)
        // when(dozerBeanMapper.map(emp1, EmployeeDTO.class)).thenReturn(dto1);
        // when(dozerBeanMapper.map(emp2, EmployeeDTO.class)).thenReturn(dto2);

        // 4. 执行测试并断言
        List<EmployeeDTO> result = employeeSearchService.fetchEmployees();
        System.out.println(result);
        assertEquals(2, result.size()); // 修正断言的size值
        assertEquals("James", result.get(0).getName()); // 可以添加更细致的断言
    } 
}

额外优化建议

  • 如果你不想Mock Dozer,而是想使用真实的Dozer实例来测试转换逻辑,可以去掉@Mock DozerBeanMapper,改用@Autowired或者手动创建真实实例,这样更贴近实际业务场景(适合需要验证转换规则正确性的测试)。
  • 可以改用@ExtendWith(MockitoExtension.class)替代@SpringBootTest和MockitoAnnotations.initMocks(this),让测试更轻量,不需要加载Spring上下文:
    @ExtendWith(MockitoExtension.class)
    public class EmployeeSearchServiceTest {
        // 其他代码不变
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:05:09