Unit Testing RxJava Observables

RxJava is a great library, but it’s not easy to get started. Here we outline different approaches to unit test Observables.

Iván Carballo
ribot labs

At ribot, we started using RxJava a few months ago and now it’s become a core element in the architecture of the Android apps we create. There are many benefits that come with it, but the learning curve is steep and quite often we still find ourselves trying to get our heads around those “beautiful” diagrams that explain how operators work.

The first step to rx-ify our architecture was to change the methods in the data layer so that they returned Observables. The next question was: how do we unit test this?

The ugly way

The first thing that came to our minds was to simply subscribe in the same way we would from outside the tests and then save the result in a global variable that can later on be asserted. For example, imagine we have a method in a database helper class that is in charge of loading a user object.

public Observable<User> loadUser() {
...
}

Then the test for this method would look like this:

User mUser;@Test
public void shouldLoadUser() throw Exception {
databaseHelper.loadUser()
.subscribe(new Action1<User>() {
@Override
public void call(User user) {
mUser = user;
}
});
assertNotNull(mUser);
}

This code works because, by default, the Observable will run on the same thread. Therefore, the assertion will always happen after the result is set in the global variable.

The better way

We soon realised that the previous solution wasn’t elegant. Even though this approach worked, it required a lot of boilerplate code. So we decided to create a small class that provides a static method called subscribeAssertingThat(). This allowed us to subscribe to an observable, save the result and then perform some assertions in a cleaner way. For example:

@Test
public void shouldLoadTwoUsers() throw Exception {
subscribeAssertingThat(databaseHelper.loadUser())
.completesSuccessfully()
.hasSize(2)
.emits(user1, user2)
}

With about 100 lines of code, this class that we called RxAssertions, made our tests much more readable and easier to write. You can find the RxAssertions code here.

The official way

Later on, we discovered that RxJava provides a specific type of subscriber called TestSubscriber.

A TestSubscriber is a variety of Subscriber that you can use for unit testing, to perform assertions, inspect received events, or wrap a mocked Subscriber.

Similar to the second solution, a test subscriber allows you to perform assertions over the result of the subscription. For example:

@Test
public void shouldLoadTwoUsers() throw Exception {
TestSubscriber<User> testSubscriber = new TestSubscriber<>();
databaseHelper.loadUser().subscribe(testSubscriber);
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(Arrays.asList(user1, user2))
}

We still haven’t used TestSubscriber on a frequent basis, but as you can see above the resulting code is quite elegant and readable. Apart from the different assertions it also allows you to retrieve the whole list of items emitted by the Observable by calling getOnNextEvents().

When using TestSubscriber you will discover that chaining assertions is not possible, because the assert methods don’t return the TestSubscriber. The ability to do this would be a nice improvement.

Conclusion

TestSusbcriber is probably the best option at the moment and because it’s part of the RxJava codebase it will continue improving and new features will be merged in. It would be good to hear how other developers are unit testing their Observables. What other methods do you use apart from those I’ve mentioned here?

Responses (7)

What are your thoughts?