Reputation: 12906
My Spring Boot application provides the following service which increments a counter on the User
entity and persists it to the database.
@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.
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. 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("[email protected]");
Assertions.assertEquals(1, xyz); // from what source would xyz come from?
}
}
Upvotes: 1
Views: 2739
Reputation: 12285
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. 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.
// 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 [email protected] when userService.incrementLoginFailures("[email protected]")
is called.
That would be done with verify
, like:
@InjectMocks
private MyService userService;
@Mock
private UserRepo userRepo;
@Mock
private User user;
@Test
public void shouldIncrementAndSave() throws UserNotFoundException {
when(userRepo.findByEmail("[email protected]")).thenReturn(Optional.of(user));
userService.incrementLoginFailures("[email protected]");
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
.
Upvotes: 1
Reputation: 24637
I recommend returning the user object from the method to help with testing. To setup mock repository behavior, use Mockito.when
and return a new user POJO. Then assert the login failure count is 1 on the returned object.
Upvotes: 1
Reputation: 547
In your test method before calling userService.incrementLoginFailures("[email protected]");
you will need to mock the return values of the methods called on userRepo something like below:
@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("[email protected]")).thenReturn(user);
userService.incrementLoginFailures("[email protected]");//this method will make changes to the user object
Assertions.assertEquals(1, user.getLoginFailures());
}
}
Upvotes: 1