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

Service层fetchGenreById方法测试失败,返回null求助排查

问题:Service层fetchGenreById方法测试返回null的排查与解决

问题背景

要测试的Service层方法:

@Override
public GenreResponse fetchGenreById(Long genreId) {
    return convertGenreToGenreResponse(findGenreEntityByGenreId(genreId));
}

private GenreResponse convertGenreToGenreResponse(Genre genre) {
    return modelMapper.map(genre, GenreResponse.class);
}

private Genre findGenreEntityByGenreId(long genreId) {
    return genreRepository.findByGenreId(genreId)
        .orElseThrow(() -> new ObjectNotFoundException("Genre with " + genreId + " id not found!"));
}

编写的测试代码:

@ExtendWith(MockitoExtension.class)
class GenreCrudServiceTest {

@Mock
private ModelMapper mockModelMapper;

@Mock
private GenreRepository mockGenreRepository;

private GenreCrudService genreCrudService;

@BeforeEach
void setUp() {
    MockitoAnnotations.openMocks(this);

    genreCrudService = new GenreCrudServiceImpl(mockModelMapper, mockGenreRepository);
}

@Test
public void GenreCrudService_FetchGenreById_ReturnsGenreResponse() {
    // Arrange
    long genreId = 1;

    Genre genre = Genre.builder()
        .genreId(genreId)
        .title("testTitle")
        .build();

    Mockito.when(mockGenreRepository.findByGenreId(any(Long.class))).thenReturn(Optional.of(genre));

    // Act
    GenreResponse genreResponse = genreCrudService.fetchGenreById(genreId);

    // Assert
    Assertions.assertThat(genreResponse).isNotNull();
}

测试报错:

Expecting actual not to be null
java.lang.AssertionError: 
Expecting actual not to be null

原因分析

你只Mock了GenreRepository的行为,但**ModelMapper是用@Mock创建的模拟对象,Mockito对模拟对象的未指定行为默认返回null**。

在convertGenreToGenreResponse方法中调用modelMapper.map(genre, GenreResponse.class)时,因为没有给这个方法设置返回值,所以返回null,最终导致fetchGenreById返回null,断言失败。

解决方案

给mockModelMapper.map方法添加Mock行为,指定它在接收Genre对象和GenreResponse.class时返回一个非null的GenreResponse实例:

修正后的测试代码:

@ExtendWith(MockitoExtension.class)
class GenreCrudServiceTest {

@Mock
private ModelMapper mockModelMapper;

@Mock
private GenreRepository mockGenreRepository;

private GenreCrudService genreCrudService;

@BeforeEach
void setUp() {
    MockitoAnnotations.openMocks(this);

    genreCrudService = new GenreCrudServiceImpl(mockModelMapper, mockGenreRepository);
}

@Test
public void GenreCrudService_FetchGenreById_ReturnsGenreResponse() {
    // Arrange
    long genreId = 1;

    Genre genre = Genre.builder()
        .genreId(genreId)
        .title("testTitle")
        .build();

    // 准备预期返回的GenreResponse实例
    GenreResponse expectedResponse = GenreResponse.builder()
        .genreId(genreId)
        .title("testTitle")
        .build();

    // Mock Repository的查询行为
    Mockito.when(mockGenreRepository.findByGenreId(any(Long.class))).thenReturn(Optional.of(genre));
    // Mock ModelMapper的转换行为
    Mockito.when(mockModelMapper.map(any(Genre.class), eq(GenreResponse.class))).thenReturn(expectedResponse);

    // Act
    GenreResponse genreResponse = genreCrudService.fetchGenreById(genreId);

    // Assert
    Assertions.assertThat(genreResponse).isNotNull();
    // 还可以进一步断言返回的对象和预期一致
    Assertions.assertThat(genreResponse.getGenreId()).isEqualTo(genreId);
    Assertions.assertThat(genreResponse.getTitle()).isEqualTo("testTitle");
}

补充说明

  • 使用any(Genre.class)匹配任意Genre类型的参数,eq(GenreResponse.class)精确匹配Class类型参数,确保Mock的行为准确触发。
  • 如果需要验证modelMapper.map是否被正确调用,可以在断言部分添加:Mockito.verify(mockModelMapper, times(1)).map(genre, GenreResponse.class);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:07:30