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

使用Mockito存根Stream API map方法时单元测试遇ClassCastException

问题分析与解决方案

错误原因

你遇到的ClassCastException是因为Mockito存根逻辑错误:
你试图对contentRepository.findAll().stream().map(...).collect(...)整个链式调用进行存根,但Mockito的链式存根逻辑会把findAll()的返回值当成你最终要返回的List<ContentDTO>,而服务层代码中repository.findAll()需要返回List<Content>,这就导致了类型强制转换失败。

正确的测试写法

单元测试服务层时,应该分别存根依赖组件的核心方法,而不是链式调用。具体修改如下:

1. 优化测试类注解(可选但推荐)

用@ExtendWith(MockitoExtension.class)替代@SpringBootTest,不需要加载完整Spring上下文,测试更高效:

@ExtendWith(MockitoExtension.class)
public class ContentServiceImplTest {

    @InjectMocks 
    private ContentServiceImpl contentService;
    
    @Mock 
    private ContentRepository contentRepository;
    @Mock
    private ContentDTOMapper contentDTOMapper;
    @Mock
    private Environment environment;

    // 测试方法写在这里
}

2. 修正测试方法逻辑

@Test
void getAllContentsValidTest(){
    // given - 准备测试数据
    Content content = new Content();
    content.setId(1);
    content.setTitle("test content title name");
    content.setDesc("test content description for a new content");
    content.setStatus(Status.PUBLISHED);
    content.setContentType(Type.ARTICLE);
    content.setDateCreated(LocalDateTime.now());
    content.setDateUpdated(LocalDateTime.of(2099, Month.DECEMBER, 15,12,12));
    content.setUrl("http://test.com");

    Content content2 = new Content();
    content2.setId(2);
    content2.setTitle("test content title name2");
    content2.setDesc("test content description for a new content2");
    content2.setStatus(Status.PUBLISHED);
    content2.setContentType(Type.ARTICLE);
    content2.setDateCreated(LocalDateTime.now());
    content2.setDateUpdated(LocalDateTime.of(2099, Month.DECEMBER, 15,12,12));
    content2.setUrl("http://test2.com");

    ContentDTO contentDTO = new ContentDTO(
            content.getTitle(),
            content.getDesc(),
            content.getStatus(),
            content.getContentType(),
            content.getDateCreated(),
            content.getDateUpdated(),
            content.getUrl()
    );

    ContentDTO contentDTO2 = new ContentDTO(
            content2.getTitle(),
            content2.getDesc(),
            content2.getStatus(),
            content2.getContentType(),
            content2.getDateCreated(),
            content2.getDateUpdated(),
            content2.getUrl()
    );

    // when - 存根依赖方法
    List<Content> contentList = List.of(content, content2);
    // 存根repository返回Content列表
    Mockito.when(contentRepository.findAll()).thenReturn(contentList);
    // 存根mapper将每个Content转换为对应DTO
    Mockito.when(contentDTOMapper.apply(content)).thenReturn(contentDTO);
    Mockito.when(contentDTOMapper.apply(content2)).thenReturn(contentDTO2);

    List<ContentDTO> actual = contentService.getAllContents();
    List<ContentDTO> expected = List.of(contentDTO, contentDTO2);

    // then - 断言结果
    Assertions.assertEquals(expected, actual);
}

额外优化建议

  • 对于简单的DTO转换逻辑,可以考虑不mockContentDTOMapper,而是直接使用真实实例(比如用@Spy或者直接new),这样测试更贴近真实场景,同时验证转换逻辑是否正确
  • 使用List.of()替代ArrayList初始化,代码更简洁
  • 确保Content和ContentDTO类重写了equals()和hashCode()方法,否则assertEquals会因为对象引用不同而断言失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 17:29:51