The TestObserver is an RxJava staple for testing.

It allows you to assert values in a stream, in the specific order they were emitted. Here’s a quick code snippet from the movies-usf repository 1:

@Test
fun onSearchingForMovieBladeRunner_shouldSeeSearchResult() {
    viewModel = MSMainVm(mockApp, mockMovieRepo)
    val viewStateTester = viewModel.viewState.test()

    viewModel.processInput(SearchMovieEvent("blade runner 2049"))

    viewStateTester.assertValueAt(1) {
        assertThat(it.searchedMovieTitle).isEqualTo("Searching Movie...")
        true
    }

    viewStateTester.assertValueAt(2) {
        assertThat(it.searchedMovieTitle).isEqualTo("Blade Runner 2049")
        // ...
        true
    }
}

If you look at the source for the base TestObserver, there are a bunch of these useful methods:

assertValue(Predicate valuePredicate): Asserts that this TestObserver/TestSubscriber received exactly one onNext value for which the provided predicate returns true.

assertValue(T value): Assert that this TestObserver/TestSubscriber received exactly one onNext value which is equal to the given value with respect to Objects.equals.

assertValueAt(int index, Predicate valuePredicate): Asserts that this TestObserver/TestSubscriber received an onNext value at the given index for the provided predicate returns true.

assertValueAt(int index, T value) Asserts that this TestObserver/TestSubscriber received an onNext value at the given index which is equal to the given value with respect to null-safe Object.equals.

assertValues(T… values) Assert that the TestObserver/TestSubscriber received only the specified values in the specified order.

assertValuesOnly(T… values) Assert that the TestObserver/TestSubscriber received only the specified values in the specified order without terminating.

While all these operators are useful, most of them require you to know the precise index of the value you’re testing. Typically though when i’m testing a feature built with a USF architecture, I really only care about the last value emitted in my test case.

When working with a more recent example in my demo app: I had a checklist that I would toggle multiple times in different permutations and combinations. Depending on the final state of the checklist, a save button would be enabled/disabled. So my test really cares only about the very last value viz. the state of the save button. Knowing the precise index value of the final result started to get tiring.

An argument could also be made that for this test, testing with the precise index value actually makes it a little brittle. Say I introduced an additional loading state (for some reason), I would have to go back and change all my tests to update the precise index number. I fully understand this is tricky territory cause some would argue you would also want to test the intermediate loading states. All I’m saying is that for these specific tests, I didn’t care about the intermediate loading state, just the final output.

So if we ran with that idea, how do you make the test less brittle?

vsTester.assertValueAt(vsTester.valueCount() - 1) {
    // predicate condition tested
    true
}

As simple as that, just test the last value using valueCount. But if you’re in Kotlin land, you’re spoilt with all those tasty elegant apis, so you definitely want to make this easier by adding an extension function like so:

fun <T> TestObserver<T>.assertLastValue(predicate: (T) -> Boolean) {
    assertValueAt(valueCount() - 1) { predicate.invoke(it) }
}

💥

Now these tests read much more fluidly:

vsTester.assertLastValue {
    // predicate condition tested
    true
}

Take a look at the DemoGenreVmTest class if you’re interested to see a bunch of these in full action.

Remember the ABC of programming - Always Be CTesting…

  1. movies-usf is a simple open source Android demo app where I demonstrate how one can build features using a Unidirectional State Flow (MVI) pattern