来自模拟 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 指出问题的解决方案。
我正在努力解决一些与 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 指出问题的解决方案。