Home > Net >  How can I unit-test Exception thrown inside a Lambda?
How can I unit-test Exception thrown inside a Lambda?

Time:09-09

I have a method:

public UserEntity authentication(final UserEntity auth)
    throws AuthenticationException, EmailNotFoundException {
    final AtomicReference<UserEntity> atomic = new AtomicReference<>();
    this.repository
        .findByEmail(auth.getEmail())
        .ifPresentOrElse(
            usr -> {
                if (Objects.equals(usr.getPassword(), auth.getPassword())) {
                    atomic.set(usr);
                } else {
                    throw new AuthenticationException();
                }
            },
            () -> {
                throw new EmailNotFoundException(auth.getEmail());
            }
        );
    return atomic.get();
}

This is how the user authorization test looks like:

@Test
void userAuthentication_success() {
    given(this.repository.findByEmail(this.user.getEmail()))
        .willReturn(Optional.of(this.user));
    assertThat(this.underTest.authentication(this.user))
        .isInstanceOf(UserEntity.class)
        .isEqualTo(this.user);
    verify(this.repository)
        .findByEmail(this.user.getEmail());
}

Is there any way to check the case when a user has entered the wrong password?

In the case when I send the wrong password, it doesn't work because given(this.repository.findByEmail(this.user.getEmail())).willReturn(Optional.of(this.user)); makes repository.findByEmail() return the result before you get to checking the password.

CodePudding user response:

You don't need this formidable multiline lambda. Having if-statement outside a lambda expression is way cleaner than having then cramming it inside the lambda.

And there's no need to utilize convoluted logic with AtomicReference unless you have an intention to perplex the reader of the code.

There are three cases: user doesn't exist, user-credentials are wrong, user's data is valid. Let's deal with them separately:

public UserEntity authentication(final UserEntity auth)
    throws AuthenticationException, EmailNotFoundException {

    UserEntity user = this.repository
        .findByEmail(auth.getEmail())
        .orElseThrow(() -> new EmailNotFoundException(auth.getEmail()));
    
    if (Objects.equals(user.getPassword(), auth.getPassword())) {
        throw new AuthenticationException();
    }
    
    return user;
}

To test if the exceptions are being thrown as expected, you can use one of the flavor of assertThrows().

Here is an example of test checking whether AuthenticationException would be thrown when user credentials are incorrect:

@Test
void userAuthenticationFailure() {
    assertThrows(AuthenticationException.class,
                 () -> this.underTest.authentication(UserWithWorngPassword),
                 "Wrong user password should trigger an Exception");
}

CodePudding user response:

First of all I would refactor your code to avoid side-effects:

public UserEntity authentication(final UserEntity auth)
    throws AuthenticationException, EmailNotFoundException {
    return this.repository
        .findByEmail(auth.getEmail())
        .map(usr -> {
           if (!Objects.equals(usr.getPassword(), auth.getPassword())) {
               throw new AuthenticationException();
           }
           return usr;
        }).orElseThrow(() -> { throw new EmailNotFoundException(auth.getEmail()); });
}

Then, I don't see an issue with mocking this.repository.findByEmail, I just think you made it return a valid user with the correct password. Something like:

given(this.repository.findByEmail(this.user.getEmail())).willReturn(Optional.of(this.user.withPassword("wrong password")));
  • Related