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

使用ModelMapper时Mockito模拟失效,如何正确Mock?

正确Mock ModelMapper的方法

针对你遇到的问题,核心是ModelMapper的map(source, destination)是**无返回值(void)**的方法,它的作用是修改传入的destination对象,而非返回新对象。Mockito中处理这类方法不能用when().thenReturn(),需要用doAnswer来模拟实际的字段赋值行为。

修正后的测试代码

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @InjectMocks
    private UserServiceImpl service;

    @Mock
    private UserRepository repository;
    @Mock
    private ModelMapper mapper;

    @Test
    void updateWithSuccess() {
        long userId = 1L;

        UpdateUserDTO requestUpdateUserDTO = UpdateUserDTO.builder().height(1f).weight(1f).build();
        User userFromDB = User.builder().id(1L).ssn("123").height(50f).weight(50f).build();
        UserDTO response = UserDTO.builder().id(1L).height(1f).weight(1f).build();

        // Mock repository.findById
        Mockito.when(repository.findById(userId)).thenReturn(Optional.of(userFromDB));

        // 模拟ModelMapper的void map方法:手动将UpdateUserDTO的字段赋值给User对象
        Mockito.doAnswer(invocation -> {
            // 获取方法的参数:第一个是UpdateUserDTO,第二个是User
            UpdateUserDTO source = invocation.getArgument(0);
            User target = invocation.getArgument(1);
            target.setHeight(source.getHeight());
            target.setWeight(source.getWeight());
            return null; // void方法返回null
        }).when(mapper).map(Mockito.any(UpdateUserDTO.class), Mockito.any(User.class));

        // Mock repository.save
        Mockito.when(repository.save(Mockito.any(User.class))).thenReturn(userFromDB);

        // Mock map User到UserDTO的方法
        Mockito.when(mapper.map(Mockito.any(User.class), Mockito.eq(UserDTO.class))).thenReturn(response);

        // 执行测试方法
        UserDTO result = service.update(userId, requestUpdateUserDTO);

        // 验证User对象的字段是否被正确更新
        assertEquals(requestUpdateUserDTO.getHeight(), userFromDB.getHeight());
        assertEquals(requestUpdateUserDTO.getWeight(), userFromDB.getWeight());

        // 验证repository.save被调用,且传入的是更新后的User
        ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
        Mockito.verify(repository).save(userCaptor.capture());
        assertEquals(requestUpdateUserDTO.getHeight(), userCaptor.getValue().getHeight());
        assertEquals(requestUpdateUserDTO.getWeight(), userCaptor.getValue().getWeight());
    }
}

关键修正点说明

  • 处理void方法的Mock:使用doAnswer拦截map方法调用,手动实现字段复制逻辑,确保传入的User对象被正确修改。
  • 修复测试结构问题:你之前把service.update和断言代码写在了@Test方法外部,这会导致编译错误,必须将执行和断言逻辑放在测试方法内部。
  • 补充缺失的Mock依赖:测试类中漏了@Mock private UserRepository repository;,这会引发NullPointerException。
  • 正确使用ArgumentCaptor:需要通过verify(repository).save(userCaptor.capture())触发捕获,才能获取到保存的User对象。

替代方案:使用真实的ModelMapper实例

如果你的测试需要验证ModelMapper的映射规则是否正确,可以不用Mock它,直接注入真实实例:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @InjectMocks
    private UserServiceImpl service;

    @Mock
    private UserRepository repository;
    // 注入真实的ModelMapper
    private ModelMapper mapper = new ModelMapper();

    @BeforeEach
    void setUp() {
        service = new UserServiceImpl(repository, mapper);
    }

    // 后续测试逻辑无需Mock mapper,直接执行即可验证映射结果
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:45:48