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

如何在Mockito测试中调用真实的MapStruct Mapper方法?

问题

我有一个服务类MyServiceImpl,其方法通过MyMapper将实体列表转换为DTO列表返回。我希望在测试中调用真实的Mapper方法以验证实际功能。
当前测试类使用@ExtendWith(MockitoExtension.class),对MyMapper标注@Mock(answer = Answers.CALLS_REAL_METHODS)并注入到待测试的MyServiceImpl中。测试时调用getActiveUserCardsById方法,预期返回4个DTO,但实际得到空列表。
请问如何仅通过MockitoExtension调用真实的MapStruct Mapper方法?


测试类代码

@ExtendWith(MockitoExtension.class)
public class UserCardServiceTest {

    @Mock
    private MyCardRepository myCardRepository;

    @Mock(answer = Answers.CALLS_REAL_METHODS)
    private MyMapper myMapper;

    @InjectMocks
    private MyServiceImpl underTest;
    
 @BeforeEach
    void setUp() {
      listOfExpiredUserCardEntities = new ArrayList<>(List.of(
                getValidUserCardEntity()
                        .setExpirationDate(LocalDate.now().minusDays(1))
                        .setBlocked(false),
                getValidUserCardEntity()
                       .setExpirationDate(LocalDate.now().minusWeeks(1))
                        .setBlocked(false),
                    
                getValidUserCardEntity()
                        .setExpirationDate(LocalDate.now().minusYears(1))
                        .setBlocked(false),
                getValidUserCardEntity()
                    .setExpirationDate(LocalDate.now().minusMonths(1))
                        .setBlocked(false));
              }

 @Test
    void shouldChangeStatusIfCardExpired() {
        
        given(myCardRepository.findAllByUserId(1L))
                .willReturn(listOfCardEntities);
        
        assertThat(underTest.getActiveUserCardsById(1L)).isNotNull().hasSize(4);
        assertThat(underTest.getActiveUserCardsById(1))
                .extracting(CardDto::isBlocked)
                .containsExactly(true, true, true, true)
                .doesNotContainNull();
    }
}

服务类方法代码

@RequiredArgsConstructor
public class MyServiceImpl implements MyCardService {

    private final MyCardRepository myCardRepository;

    private final MyMapper myMapper;

    @Override
    public List<CardDto> getActiveUserCardsById(final long userId) {
       
        List<CardEntity> listOfEntities = myRepository.findAllByUserId(userId);

        listOfEntities.stream().filter(validationService::isCardExpired)
                .forEach(userCard -> userCard.setBlocked(true));

        List<CardEntity> sorted = listOfEntities.stream()
                .sorted(o -> o.getName().toUpperCase())
                .toList();

        return myMapper
                .listOfCardEntitiesToCardDto(sorted);
    }
}

测试报错信息

java.lang.AssertionError: 
Expected size: 4 but was: 0 in:
[]

解决方案

核心问题是:MapStruct的MyMapper是接口,Mockito的@Mock(answer = Answers.CALLS_REAL_METHODS)对接口无效——接口本身没有方法实现,代理对象调用时只会返回默认值(比如空列表),这就是你得到空结果的原因。

要在MockitoExtension环境中调用真实的MapStruct Mapper方法,有两种可靠方式:

方式一:直接使用真实的Mapper实现类实例

MapStruct会自动生成Mapper接口的实现类(默认命名为[Mapper接口名]Impl,比如MyMapperImpl),直接创建该实例并注入服务即可:

@ExtendWith(MockitoExtension.class)
public class UserCardServiceTest {

    @Mock
    private MyCardRepository myCardRepository;

    // 直接实例化真实的Mapper实现类
    private MyMapper myMapper = new MyMapperImpl();

    @InjectMocks
    private MyServiceImpl underTest;
    
    // 其余测试代码保持不变
}

方式二:用Mockito @Spy包装真实实例

如果需要保留Mockito的部分特性(比如偶尔打桩特定方法),可以用@Spy包装真实Mapper实例,默认会调用真实方法:

@ExtendWith(MockitoExtension.class)
public class UserCardServiceTest {

    @Mock
    private MyCardRepository myCardRepository;

    // @Spy包装真实实例,默认执行真实方法逻辑
    @Spy
    private MyMapper myMapper = new MyMapperImpl();

    @InjectMocks
    private MyServiceImpl underTest;
    
    // 其余测试代码保持不变
}

额外修复点

你的测试代码中还存在变量名不一致问题:setUp方法初始化的是listOfExpiredUserCardEntities,但测试中given语句返回的是listOfCardEntities,这会导致仓库返回空列表,需要把变量名统一,比如将listOfExpiredUserCardEntities改为listOfCardEntities。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 17:50:37