MeanwhileInHell
MeanwhileInHell

Reputation: 7053

Spring WebFlux; unit testing exception thrown in Mono.map()

I have some code that returns Mono<List<UserObject>>. The first thing I want to do is check the List is not empty, and if it is, throw a NoUsersFoundException. My code looks like this:

IUserDao.java
Mono<List<UserAccount>> getUserProfiles(final Set<UserQueryFilter> filters,
                                       final Set<String> attributes);
GetUserAccount.java
public Mono<UserAccount> doGetUserAccount() {
    return userDao.getUserProfiles(filters, attributes)
                      .map(list -> {
                          if (CollectionUtils.isEmpty(list)) {
                              throw new NoUsersFoundException();
                           }
                           return list;
                       })
                       .map(this::removePermissions)
                       .map(this::removeDuplicates);
}

I want to write a unit test that will test that the NoUsersFoundException is thrown when userDao.getUserProfiles(filters, attributes) returns an empty list. When I use Mockito#when with a .thenReturn(), the test will, as expected, return immediately once userDao.getUserProfiles(...) is called without continuing the flow into the .map() where the list is checked and exception thrown.

@Mock
private IUserDao userDao;

private UserPolicies userPolicies;

@BeforeEach
public void init() {

    userPolicies = new UserPolicies(Set.of("XYZ", USER_AFF, "123"),
                                           Set.of(TestUserConstants.ID, TestUserConstants.SUBSCRIPTION_LEVEL));
}

@Test
void shouldThrowExceptionIfNoUsersFound() {

    final Set<UserFilter> filters = new UserFilterBuilder().withId(ID)
                                                           .withSubscription(PREMIUM)
                                                           .build();

    when(userDao.getUserProfiles(filters, userPolicies.getUserAttributeIds()))
                    .thenReturn(Mono.just(Collections.emptyList()));

    testClass = new GetUserAccount(userDao,
                                   userPolicies,
                                   filters,
                                   userPolicies.getUserAttributeIds());

    assertThatThrownBy(() -> testClass.doGetUserAccount()).isInstanceOf(NoUsersFoundException.class);
}

I have tried .thenAnswer() but it essentially does the same thing as the method called is not a void:

userDao.getUserProfiles(filters, userPolicies.getUserAttributeIds()))
                .thenAnswer((Answer<Mono<List>>) invocationOnMock -> Mono.just(Collections.emptyList()));

I can't see how using reactor.test.StepVerifier would work for this case.

Upvotes: 1

Views: 3800

Answers (1)

Toerktumlare
Toerktumlare

Reputation: 14712

i dont really understand what you are asking for, but we commonly dont "throw" exceptions in reactor. We return a Mono#error downstream, and different operators will react accordingly as the error travels downstream.

public Mono<List<Foobar> fooBar(filters, attributes) {
    return daoObject.getUserProfiles(filters, attributes)
                      .map(list -> {
                          if (CollectionUtils.isEmpty(list)) {
                              // Return a mono#error
                              return Mono.error( ... );
                           }
                           return list;
                       })
}

And then test using the step verifier. With either expectNext or expectError.

// Happy case
StepVerifier.create( 
    fooBar(filters, attributes)) 
    .expectNext( ... ) 
    .verify();

// Sad case
StepVerifier.create( 
    fooBar(filters, attributes)) 
    .expectError( ... ) 
    .verify();

Upvotes: 2

Related Questions