简体   繁体   English

在 Spring Boot 中使用模拟存储库测试 void 服务方法?

[英]Testing a void service method with mocked repository in Spring Boot?

My Spring Boot application provides the following service which increments a counter on the User entity and persists it to the database.我的 Spring 引导应用程序提供以下服务,该服务增加User实体上的计数器并将其持久化到数据库中。

@Service
public class MyService {
    
    @Autowired
    UserRepo userRepo;
    
    public void incrementLoginFailures(String email) throws UserNotFoundException {
        final User user = userRepo.findByEmail(email).orElseThrow(UserNotFoundException::new);
        user.incrementLoginFailures();
        userRepo.save(user);
    }
}

Now I'd like to write a JUnit 5 test for this, preferrably mocking the respository but I guess I have some trouble understanding the concept of mocking here.现在我想为此编写一个 JUnit 5 测试,最好是 mocking 存储库,但我想我在这里理解 mocking 的概念有些困难。

As you can see, the service method incrementLoginFailures() does not return anything so I don't know how I could check that the login counter on the User entity was actually incremented.如您所见,服务方法incrementLoginFailures()不返回任何内容,因此我不知道如何检查User实体上的登录计数器是否实际增加了。 Do I need to write an integration test for this kind of check which actually spins up a database?我是否需要为这种实际上启动数据库的检查编写集成测试?

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

  @InjectMocks
  private UserService userService;

  @Mock
  private UserRepo userRepo;

  @Test
  public void shouldIncrement() {
    userService.incrementLoginFailures("foo@bar.com");
    Assertions.assertEquals(1, xyz);  // from what source would xyz come from?
  }
}

In your test method before calling userService.incrementLoginFailures("foo@bar.com");在调用userService.incrementLoginFailures("foo@bar.com");之前的测试方法中you will need to mock the return values of the methods called on userRepo something like below:您将需要模拟 userRepo 上调用的方法的返回值,如下所示:

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

  @InjectMocks
  private UserService userService;

  @Mock
  private UserRepo userRepo;

  @Test
  public void shouldIncrement() {
    User user = new User();//please initialize as per your requirement
    when(userRepo.findByEmail("foo@bar.com")).thenReturn(user);
    userService.incrementLoginFailures("foo@bar.com");//this method will make changes to the user object
    Assertions.assertEquals(1, user.getLoginFailures());
  }
}

I recommend returning the user object from the method to help with testing.我建议从方法中返回用户 object 以帮助测试。 To setup mock repository behavior, use Mockito.when and return a new user POJO.要设置模拟存储库行为,请使用Mockito.when并返回新用户 POJO。 Then assert the login failure count is 1 on the returned object.然后在返回的 object 上断言登录失败计数为 1。

You should only test your own code and in unit test the code for that unit.您应该只测试您自己的代码,并在单元测试该单元的代码。 So not for example if Spring data repository save() works.因此,例如,如果 Spring 数据存储库save()有效,则不是。 You can test it but you should trust that it works.你可以测试它,但你应该相信它是有效的。

If done correct you should have a unit test that asserts that user.incrementLoginFailures() works as it should.如果做得正确,你应该有一个单元测试断言user.incrementLoginFailures()可以正常工作。

// Unit test for the user
@Test
public void shouldIncrement() {
    User user = new User();
    // In here when you have just initialized user you might know that 
    // the initial count is zero but it is overall better to assert 
    // the logic for any value / case
    int originalLoginFailures = user.getLoginFailures();
    user.incrementLoginFailures();
    assertEquals(originalLoginFailures + 1, user.getLoginFailures());
 }

That is actually what you need to know in addition that the data is saved to the database.除了将数据保存到数据库之外,这实际上是您需要知道的。

So you might have a unit test for UserService (or is it MyService?) that asserts that userIncrementLoginFailures() and userRepo.save() is called for user with email foo@bar.com when userService.incrementLoginFailures("foo@bar.com") is called.所以你可能有一个UserService的单元测试(或者它是 MyService?),它断言userIncrementLoginFailures()userRepo.save()是为用户调用 email foo@bar.comuserService.incrementLoginFailures("foo@bar.com")被调用。

That would be done with verify , like:这将通过verify来完成,例如:

@InjectMocks
private MyService userService;
@Mock
private UserRepo userRepo;
@Mock
private User user; 

@Test
public void shouldIncrementAndSave() throws UserNotFoundException {
    when(userRepo.findByEmail("foo@bar.com")).thenReturn(Optional.of(user));
    userService.incrementLoginFailures("foo@bar.com");
    verify(user).incrementLoginFailures();
    verify(userRepo).save(user);
}

You could test that user.incrementLoginFailures() works in here also, but because you tested it in your user unit test it is not necessarily needed but is a redundant test and tests a logic that does not belong to unit tested, namely UserService .您可以测试user.incrementLoginFailures()也可以在这里工作,但是因为您在用户单元测试中对其进行了测试,所以不一定需要它,而是一个冗余测试,并测试不属于单元测试的逻辑,即UserService

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM