Java 如何在单元测试中模拟 JPA 存储库的保存方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51247796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 00:02:44  来源:igfitidea点击:

How to mock JPA repository's save method in unit tests

javaspringunit-testingmockito

提问by ip696

For example, I have this method in UserService:

例如,我在 UserService 中有这个方法:

  @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;
  }

How can I create unit test for this method? I tried next:

如何为此方法创建单元测试?我接下来尝试:

  @Test
  public void createUser() {

    UserDto userDtoRequest = new UserDto();
    userDtoRequest.setLogin("Alex");
    userDtoRequest.setPassword("123");

    UserDto found = userService.create(userDtoRequest);
    assertThat(found.getAuthToken()).isNotEmpty();
}

I have next logic:

我有下一个逻辑:

  1. Test start
  2. User dbUser = userRepository.findOne(userDto.getId());dbUser = NULL
  3. if (dbUser != null) and if (oneByLogin != null)skip
  4. create new user and set data
  5. User savedUser = userRepository.save(newUser);savedUser = NULL
  1. 测试开始
  2. User dbUser = userRepository.findOne(userDto.getId());数据库用户 = NULL
  3. if (dbUser != null) 并if (oneByLogin != null)跳过
  4. 创建新用户并设置数据
  5. User savedUser = userRepository.save(newUser);已保存用户 = NULL

On this step, I have a problem because I cannot mock userRepository.save(newUser).

在这一步中,我遇到了问题,因为我无法模拟userRepository.save(newUser).

newUser create inside the method. and test fail.

savedUser.getAuthToken() - savedUser == NULL

savedUser.getAuthToken() - savedUser == NULL

I can rewrite:

我可以重写:

    userRepository.save(newUser);
    userDto.setAuthToken(newUser.getAuthToken());
    log.info("User {0} created", newUser.getLogin());
    return userDto;

but what if I want to use the returned object savedUser?

但是如果我想使用返回的对象savedUser呢?

回答by rieckpil

You can do the following:

您可以执行以下操作:

@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(UserDto.class)).thenReturn(userToReturnFromRepository);

     UserDto found = userService.create(userDtoRequest);

     // ... your asserts

  }

}

with this approach you just have to make sure your mockedUserRepositorygets injected into your class under test (e.g. in the constructor).

使用这种方法,你只需要确保你mockedUserRepository被注入到你的被测类中(例如在构造函数中)。

回答by codeLover

You need to write multiple test cases in order to test different scenarios.

您需要编写多个测试用例来测试不同的场景。

Scenario 1: when findOnereturns a not null object:

场景一:当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);
}

Scenario 2: when findOneByLoginreturns a null object:

场景二:当findOneByLogin返回一个空对象时:

@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);
}

Scenario 2: when saveis done:

场景2:保存完成后:

@Test

@测试

public void testCreateUserWhenSaved()    {
     //Create one sample userDto object with test data
     when(mockedUserRepository.findOne(userDto.getId())).thenReturn(null);
     when(mockedUserRepository.findOneByLogin(userDto.getId())).thenReturn(null);

     //Create sample User object and set all the properties
     User newUser=new User();
     when(mockedUserRepository.save(Mockito.any(User.class)).thenReturn(newUser);
     User returnedUser=userService.create(userDto);
     //You have two ways to test, you can either verify that save method was invoked by 
     //verify method
     verify(mockedUserRepository, times(1)).save(Mockito.any(User.class);
     //or by assertion statements, match the authToken in the returned object to be equal 
     //to the one set by you in the mocked object
     Assert.assertEquals(returnedUser.getAuthToken(),newUser.getAuthToken());
}

回答by spajdo

You need to do this.

你需要这样做。

when(userRepository.save(Mockito.any(User.class)))
                .thenAnswer(i -> i.getArguments()[0]);

And now you can obtain user which you pass as argument.

现在您可以获得作为参数传递的用户。