来自模拟 ModelMapper returns 空值的 map() 方法

map() method from mocked ModelMapper returns null value

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

这是我的片段 AccountServiceTest class:

@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(...) 方法 returns 空对象,因此它抛出臭名昭著的 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 中的依赖项被正确模拟了。

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

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

例如使用 BDDMockito:

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

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

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

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