In an unit test, how to wait for an RxJava2 Observable to finish before asserting results

Viewed 2920

I'm trying to test a part of my code that subscribe to a cold observable. My problem is that the unit test is not waiting for an internal observable call to end before asserting the result, therefor my test fails.

Here's the code I want to test:

public class UserManager {

    private UserRepository userRepository;
    private PublishSubject<List<User>> usersSubject = PublishSubject.create();

    public UserManager(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Observable<List<User>> users() {
        return usersSubject;
    }

    public void onUserIdsReceived(List<String> userIds) {
        userRepository.getUsersInfo(userIds)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new SingleObserver<List<User>>() {
                @Override
                    public void onSubscribe(@NonNull Disposable d) {}

                    @Override
                    public void onSuccess(@NonNull List<User> users) {
                        usersSubject.onNext(users);
                    }

                    @Override
                    public void onError(Throwable e) {}
                }
    }
}

And here's my test:

@RunWith(MockitoJUnitRunner::class)
class UserManagerTest {

private val userRepository = mock(UserRepository::class.java)

companion object {
    @BeforeClass
    @JvmStatic
    fun setup() {
        RxJavaPlugins.setIoSchedulerHandler({ _ -> TestScheduler() })
        RxJavaPlugins.setSingleSchedulerHandler({ _ -> TestScheduler() })
        RxJavaPlugins.setNewThreadSchedulerHandler({ _ -> TestScheduler() })
        RxJavaPlugins.setComputationSchedulerHandler({ _ -> TestScheduler() })
        RxAndroidPlugins.setInitMainThreadSchedulerHandler({ _ -> TestScheduler() })
        RxAndroidPlugins.setMainThreadSchedulerHandler({ _ -> TestScheduler() })
    }
}

@Test
fun testReceivedUserIdResultsInPushedUser() {
    // Given
    val usersIds = mutableListOf<String>()
    usersIds.add("id1")
    val usersDetails = mutableListOf<User>()
    usersDetails.add(User.testUser())
    given(userRepository.getUsersInfo(usersIds)).willReturn(Single.just(usersDetails))

    val userManager = UserManager(userRepository)
    val testObserver = TestObserver<List<User>>()
    userManager.users().subscribeWith(testObserver)

    // When
    userManager.onUsersIdsReceived(usersIds)

    // Then
    testObserver.assertNoErrors()
    testObserver.assertNotComplete()
    testObserver.assertValueCount(1)
    testObserver.assertValue( { detectedUsers: List<DetectedUser> ->
        // My test
    })
}

My test fails because my testObserver don't receive any events, and the most logical reason is that the test doesn't wait for the userRepository.getUsersInfo(userIds) calls to end, even if I mocked the UserRepository and used a TestScheduler. Is there a way to wait for the internal Observable call to ends, thus making my code testable?

1 Answers

I'm using trampoline instead of TestScheduler like this and it executes synchronously.

// override Schedulers.io()
RxJavaPlugins.setIoSchedulerHandler(schedulerCallable -> Schedulers.trampoline());
// override AndroidSchedulers.mainThread()
RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline());

Also consider removing RxAndroidPlugins.setMainThreadSchedulerHandler({ _ -> TestScheduler() }) I think I had some problems with that.

Related