Rule
Rule

Reputation: 630

Sequence of operations with RxJava

I want to do a sequence of operations. The next one can only execute if the previous was successful. To improve my understanding I decided to try to implement it with RxJava.

The operations, in order:

  1. login
  2. store the login result
  3. use the login result to retrieve user info
  4. store user info

For this I use the following:

public interface LoginRepository {

    Single<LoginResponseEntity> login(@NonNull final Credentials credentials);

    Completable storeLoginResult(@NonNull final LoginResponseEntity loginEntity);
}

and

public interface UserSettingsRepository {

    Single<UserInfoEntity> retrieveUserInfo(@NonNull final String email, final String accessToken);

    Completable storeUserInfo(@NonNull final UserInfoEntity userInfoEntity);
}

and the Login usecase:

public Completable execute(@NonNull final Credentials credentials) {
    return loginRepository.login(credentials)
            .flatMap(loginEntity -> loginRepository.storeLoginResult(loginEntity)
                    .andThen(userSettingsRepository.retrieveUserInfo(credentials.getEmail(),
                            loginEntity.getAccessToken()))
            ).flatMapCompletable(userSettingsRepository::storeUserInfo)
            .andThen(saveIsAlreadyLogged.execute());
}

So to test this, I have created unit tests (in Kotlin). All pass, except these two:

@Test
fun `when storing login result failed, don't retrieve user info`() {
    val exception = Exception()
    whenever(loginRepository.login(any())).thenReturn(Single.just(loginResponseEntity()))
    whenever(loginRepository.storeLoginResult(any())).thenReturn(Completable.error(exception))
    whenever(saveIsAlreadyLogged.execute()).thenReturn(Completable.complete())

    val test = login.execute(credentials()).test()

    verify(userSettingsRepository, never()).retrieveUserInfo(anyString(), anyString())
}

error:

org.mockito.exceptions.verification.NeverWantedButInvoked: 
userSettingsRepository.retrieveUserInfo(
    <any string>,
    <any string>
);

and

@Test
fun `when storing login result failed, login should error`() {
    val exception = Exception()
    whenever(loginRepository.login(any())).thenReturn(Single.just(loginResponseEntity()))
    whenever(loginRepository.storeLoginResult(any())).thenReturn(Completable.error(exception))
    whenever(saveIsAlreadyLogged.execute()).thenReturn(Completable.complete())

    val test = login.execute(credentials()).test()

    test.assertError(exception)
}

error:

java.lang.AssertionError: Error not present (latch = 0, values = 0, errors = 1, completions = 0)

Do you know what I'm doing wrong? And do you have tips on how to improve my code? Please give a clear explanation as I'm still learning.

Upvotes: 0

Views: 1335

Answers (1)

marianosimone
marianosimone

Reputation: 3606

For the first case, the problem is in:

.andThen(
    userSettingsRepository.retrieveUserInfo(
        credentials.getEmail(),
        loginEntity.getAccessToken()
    )
)

Note that the call to retrieveUserInfo is done regardless of whatever happens to what you have before it. In other words: this is executed while your stream is being setup, and not when it's being run. You probably want to use Observable.defer to inside the then to match your expectations.

For the second problem, you can see that there IS an error happening:

java.lang.AssertionError: Error not present (latch = 0, values = 0, errors = 1, completions = 0)

errors = 1 is indicating that, and you'll need to look at the full message to see what the error is (as @akarnokd pointed out)

Upvotes: 1

Related Questions