Java 如何模拟JPA存储库';单元测试中的save方法
例如,我在UserService中有此方法:Java 如何模拟JPA存储库';单元测试中的save方法,java,spring,unit-testing,mockito,Java,Spring,Unit Testing,Mockito,例如,我在UserService中有此方法: @Override @Transactional public UserDto create(UserDto userDto) { User dbUser = userRepository.findOne(userDto.getId()); if (dbUser != null) { throw new AuthException(AuthException.ErrorCode.DUPLICATE_USER
@Override
@Transactional
public UserDto create(UserDto userDto) {
User dbUser = userRepository.findOne(userDto.getId());
if (dbUser != null) {
throw new AuthException(AuthException.ErrorCode.DUPLICATE_USER_EXCEPTION);
}
User oneByLogin = userRepository.findOneByLogin(userDto.getLogin());
if (oneByLogin != null) {
throw new AuthExceptionAuthException.ErrorCode.DUPLICATE_LOGIN_EXCEPTION);
}
User newUser = new User();
newUser.setGuid(UUID.randomUUID().toString());
newUser.setInsertDate(new Date());
newUser.setFirstName(userDto.getFirstName());
newUser.setLastName(userDto.getLastName());
newUser.setLogin(userDto.getLogin());
newUser.setPassword(userDto.getPassword());
newUser.setAuthToken(TokenGenerator.nextToken());
newUser.setAuthTokenCreatedDate(new Date());
User savedUser = userRepository.save(newUser);
userDto.setAuthToken(savedUser.getAuthToken());
log.info("User {0} created", savedUser.getLogin());
return userDto;
}
如何为该方法创建单元测试?我下一步尝试:
@Test
public void createUser() {
UserDto userDtoRequest = new UserDto();
userDtoRequest.setLogin("Alex");
userDtoRequest.setPassword("123");
UserDto found = userService.create(userDtoRequest);
assertThat(found.getAuthToken()).isNotEmpty();
}
我有下一个逻辑:
User dbUser=userRepository.findOne(userDto.getId())代码>dbUser=NULL
if(dbUser!=null
)和if(oneByLogin!=null)
跳过User savedUser=userRepository.save(newUser)代码>savedUser=NULL
userRepository.save(newUser)
savedUser.getAuthToken()-savedUser==NULL
我可以重写:
userRepository.save(newUser);
userDto.setAuthToken(newUser.getAuthToken());
log.info("User {0} created", newUser.getLogin());
return userDto;
但是如果我想使用返回的对象savedUser怎么办?您可以执行以下操作:
@RunWith(MockitoJUnitRunner.class)
public class SimpleTest {
@Mock
private UserRepository mockedUserRepository;
// .. your test setup
@Test
public void testYourMethod() {
User userToReturnFromRepository = new User();
userToReturnFromRepository.setAuthToken(YOUR_TOKEN);
when(mockedUserRepository.save(any(User.class)).thenReturn(userToReturnFromRepository);
UserDto found = userService.create(userDtoRequest);
// ... your asserts
}
}
使用这种方法,您只需确保您的
mockedUserRepository
被注入到被测试的类中(例如,在构造函数中)。您需要编写多个测试用例来测试不同的场景
场景1:当findOne返回非空对象时:
@Test(expected=AuthException.class)
public void testCreateUserWhenAvailable() {
//Create one sample userDto object with test data
when(mockedUserRepository.findOne(userDto.getId())).thenReturn(new User());
userService.create(userDto);
}
@Test(expected=AuthException.class)
public void testCreateUserWhenLoginAvailable() {
//Create one sample userDto object with test data
when(mockedUserRepository.findOne(userDto.getId())).thenReturn(null);
when(mockedUserRepository.findOneByLogin(userDto.getId())).thenReturn(new User());
userService.create(userDto);
}
场景2:当findOneByLogin返回空对象时:
@Test(expected=AuthException.class)
public void testCreateUserWhenAvailable() {
//Create one sample userDto object with test data
when(mockedUserRepository.findOne(userDto.getId())).thenReturn(new User());
userService.create(userDto);
}
@Test(expected=AuthException.class)
public void testCreateUserWhenLoginAvailable() {
//Create one sample userDto object with test data
when(mockedUserRepository.findOne(userDto.getId())).thenReturn(null);
when(mockedUserRepository.findOneByLogin(userDto.getId())).thenReturn(new User());
userService.create(userDto);
}
场景2:save完成时:
@试验
你需要这样做
when(userRepository.save(Mockito.any(User.class)))
.thenAnswer(i -> i.getArguments()[0]);
现在,您可以获得作为参数传递的用户。关于如何创建JPA存储库
save
方法,以及为具有@GeneratedValue
的字段生成随机ID,我只需给您两分钱
/**
* Mocks {@link JpaRepository#save(Object)} method to return the
* saved entity as it was passed as parameter and add generated ID to it.
* If ID could not be generated, it will be ignored.
* If parameter already has and ID, it will be overridden.
*/
private <T, V> void mockSave(JpaRepository<T, V> repository) {
when(repository.save(any())).thenAnswer(i -> {
Object argument = i.getArgument(0);
Arrays.stream(argument.getClass().getDeclaredFields())
.filter(f -> f.getAnnotation(GeneratedValue.class) != null)
.forEach(f -> enrichGeneratedValueField(argument, f));
return argument;
});
}
在本例中,我只使用了类型为
Integer
的ID,但可以自由填充以添加所需类型的ID。您正在测试您的服务,因此您应该像模拟存储库一样模拟其依赖关系。通过模拟方法save
,您可以指定它返回的对象。@grape\u mao以及如何执行?如果newUser对象创建了内部服务方法,而我的测试中没有该方法,那么如何调用userRepository.save(newUser)方法。当(userService.save(?)。然后返回(?);如果您不能为您的方法编写测试,这表明您需要将它分成几个部分,并分别进行测试。@ip696您有两个选择。1.忽略传入的参数,返回带有标记的用户
。2.使用类似于doAnswer
的方法来模拟该方法,以便捕获参数。此处可能重复最完整的答案。相同的过程,但可以在此处找到更可读的函数returnFirstArg(),这是服务内部考虑实体到DTO的转换吗?repo的“保存返回实体”不是DTO,服务应将其转换
private void enrichGeneratedValueField(Object argument, Field field) {
try {
if (field.getType().isAssignableFrom(Integer.class)) {
FieldUtils.writeField(field, argument, Math.abs(random.nextInt()), true);
} else {
FieldUtils.writeField(field, argument, mock(field.getType()), true);
}
} catch (Exception ignored) {
}
}