【问题标题】:map() method from mocked ModelMapper returns null value来自模拟 ModelMapper 的 map() 方法返回空值
【发布时间】:2022-01-08 19:09:08
【问题描述】:

我正在努力解决一些与 Mockito 和 JUnit 5 与 Spring Boot 应用程序相关的奇怪问题。

这是我的AccountServiceTest 类的片段:

@ExtendWith(MockitoExtension.class)
@Import(MapperConfig.class)
class AccountServiceTest {

    @Mock
    private AccountRepository accountRepository;

    @Mock
    private ModelMapper modelMapper;

    @Mock
    private PasswordEncoder passwordEncoder;

    @Mock
    private AuthenticationManager authenticationManager;

    @Mock
    private JwtTokenUtil jwtTokenUtil;

    @InjectMocks
    private AccountService underTest;

    @Test
    void itShouldCreateAccountAndAddItToDatabase() {
        // given
        CreateAccountDto dto = new CreateAccountDto("service.tester@test.com", "service.tester", "12345");

        // when
        Account account = underTest.create(dto);
        Account found = accountRepository.findByUsername(account.getUsername()).orElseThrow();

        // then
        assertEquals(found, account);
    }
    ...
}

这是我要测试的服务:

@Service
public class AccountService implements FieldValueExists {

    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private ModelMapper modelMapper;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    public Account create(CreateAccountDto dto) {
        Account account = modelMapper.map(dto, Account.class);
        account.setHashedPassword(passwordEncoder.encode(dto.getPassword()));

        accountRepository.save(account);

        return account;
    }
    ...
}

在控制器中使用时,AccountService 本身似乎一切正常,但在测试单元中,modelMapper.map(...) 方法返回 null 对象,因此它抛出了一个臭名昭著的 NullPointerException

我还设法将此映射器的配置放在MapperConfig

@Configuration
public class MapperConfig {

    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();

        /*
          Account's related mappings
         */
        modelMapper
                .typeMap(CreateAccountDto.class, Account.class);

        modelMapper
                .typeMap(Account.class, AccountDto.class);

        modelMapper
                .typeMap(Account.class, AuthorDto.class);

        /*
          Note's related mappings
         */
        modelMapper
                .typeMap(CreateNoteDto.class, Note.class);

        modelMapper
                .typeMap(Note.class, NoteDto.class);

        modelMapper
                .typeMap(EditNoteDto.class, Note.class)
                .addMappings(mapper -> {
                    mapper.when(ctx -> ctx.getSource() != null).map(EditNoteDto::getTitle, Note::setTitle);
                    mapper.when(ctx -> ctx.getSource() != null).map(EditNoteDto::getContent, Note::setContent);
                });


        return modelMapper;
    }
}

我不知道出了什么问题,因为AccountServiceTest 中的依赖项被正确地模拟了,我想。

【问题讨论】:

  • 仅仅注入模拟是不够的,我们还必须配置模拟。我建议阅读关于 mockito 的教程,例如this one from baeldung.com.
  • 你的意思是每个模拟都必须定义每个方法在我的测试单元中应该做什么?如果没有,请告诉我应该在 Baeldung 页面上查看什么。
  • 全部阅读。学习框架。或者不要使用它。如果我们在不理解框架的情况下使用框架,很可能我们不是在使用而是在滥用它们。
  • 目前我只想知道我的主要问题的答案。当我使用 .when().thenReturn() 方法组合时,从之前的文档和您发送的文档中学习,现在它可以正常工作了。它是我的问题的解决方案吗?

标签: java spring-boot junit mockito modelmapper


【解决方案1】:

对 ModelMapper 使用 @Autowired 而不是 @Mock,看看问题是否解决

【讨论】:

  • 不,它不能解决问题。和以前一样。
【解决方案2】:

实际上,对于我们要测试的参与服务的每个方法,都需要模拟您自己的实现。

例如使用 BDDMockito:

given(modelMapper.map(dto, Account.class))
    .willReturn(/*some object we want to return*/)

这是我们单元测试中唯一需要的东西,因为我们只想检查我们正在测试的东西的逻辑。

为了测试我们服务的每个部分,我们必须编写集成测试。

是的,这是错误的,我要感谢 @Turing85 为我指出问题的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-15
    • 1970-01-01
    • 2019-04-25
    • 2022-12-09
    • 2022-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多