RxJava2: A way to modular and testable code

Sachin Chandil
AndroidPub
Published in
5 min readMar 12, 2018

--

This article assumes that you are familiar with RxJava and unit tesing.

In my early phase of learning RxJava2, I came across to an operator compose() in this well explained article don’t break the chain. Frankly at first after reading this article, i had a confusion in my mind between flatMap() and compose(). I just gave up very easily here and moved on(wasn’t really a good idea).

So I was Rxifying my code in previous apps, everything was going perfect until i came across to a situation where i had to combine multiple operations. As we all developers have a god gifted sense of realising a situation when something is wrong in our code. I felt it too when i ended up having following code ( Just emphasise on flatMaps right now ):

(Above code is just to have a simple example) So at first what i can figure out is: “ it’s so damn huge” that leads to unreadable code. Here one can argue that second flatMap(), in above code, can be converted into a function and it can be shrinked down a bit further: YES, you are right, but it is a Conventional Approach. Actually this is what i did when i did not know about compose() operator(e.g. repo.saveFood(), repo.insertFood() and repo.insertServingDetails(), those return Observable). If i couldn’t have done that, the code above could have been thrice as of it’s current size.

What if i tell you that above snippet can be converted into following:

Yes this is possible using compose() operator and it is so concise and readable that you just see and you know whats is this code doing. More importantly you have your code in units(modules) now (like saveFood, getFoodList etc) those are highly unit testable and reusable.

So lets dive into the Action:

I will start from scratch so that you do not end up having same doubt as i had between flatMap() and compose(). compose() method accepts Transformer interface (ObservableTransformer, SingleTransformer, CompletableTransformer, FlowableTransformer etc.) object as an argument. Lets see a simple implementation of SingleTransformer :

val transformer = object : SingleTransformer<String, Int> {
override fun apply(upstream: Single<String>): SingleSource<Int>{
TODO("not implemented") // do your Rx stuff here and return a SingleSource
}
}

As we can see here, apply method accepts an upstream as Single<String> , this is actually a same upstream that we invoke compose() on. And in apply method we can use flatMap or any other operator to chain our Observables. Like following:

Snippet 1:

val transformer = object : SingleTransformer<String, Int> {
override fun apply(upstream: Single<String>): SingleSource<Int>{
return upstream.flatMap { Single.just(1) }
}
}

Lets dive more deeper, here is the implementation of compose() operator in Single class of RxJava lib.

Snippet 2:

We can see that the same instance of Single(this) is passed into apply method invocation, the compose() method was invoked on. So in apply method, any operator as usual can be chained like snippet 1 example upstream.flatMap { Single.just(1) }. Actually it’s same as we simply do without compose(). But now with compose() we can have code broken up in pieces (If you feel a bit lost here, just read it from the above section again, or feel free to contact me / leave a comment in case of any doubt).

So i hope we have understood how compose works and how to create Transformers. We must get back to the above task of simplifying observable chain.

Please note that code in above explanation of compose operator is in Java as RxJava is written in Java and we are using Kotlin language for our problem. I will be using lambda expression here to skip out boilerplate code.

We can create following Transformers by observing our first Conventional code example:

And after using above methods the result is the following clean code(we have already seen it in above showcase example):

Now this code just looks like a pseudo code, where you just read it and understand it all. All those transformer methods can be reused with other Observable chains also and even independently if needed. Like:

 Observable.just(PersonalisedFood(id, foodName, ..))
.compose(saveFood())
.subscribe()

So easy hah… . The same thing you can do to test saveFood() method. Testing them individually later saves our time to debug a bug as we just need to look for the method for which the test is failing.

Lets do testing…

Please note: if you are new to Mocking/RxJava testing etc. I would suggest to read about it before implementing the tests that follow, as i have skipped ton of code like overriding RxJava and RxAndroid plugins and mocking database classes etc. If you want me write about RxJava testing or Mocking do let me know in comments, i would love to help you with that or you can check out this article for testing RxJava and mocking.

Now we can write our unit test as follow:

@Test
fun testFoodSave(){
// testing saveFood() here that is in a ViewModel class
Observable.just(PersonalisedFood(testId, foodName, ..))
.compose(viewModel.saveFood())
.subscribe()
// get data to check if saveFood() actually works
viewModel.getFood(testId)
.subscribe()
// assert if id associated with data matches
assertEquals(testId, viewModel.foodData.id)
}

There are other ways of testing it also, for example using TestSubscriber but its not in the scope of this article.
You can read following RxJava testing article: https://medium.com/@fabioCollini/testing-asynchronous-rxjava-code-using-mockito-8ad831a16877

Example

Lets have a simple example:

So following is the dummy(mocking actual code as comments) snippet that iterates though a list and for each item it makes a network call, maps the data to desired POJO and then save it into database.

Now we want to break down above code into pieces using Transformer interface so that we can use compose() operator.

Following snippet shows how a flatMap block can be broken down from the chain with the help of Transformer interface. Do note the generics type of ObservableTransformer<T,E>, were T in upstream data type and E is downstream data type.

The only thing we need to do is to take flatMap operator with it’s block and just chain it with it (shortcut to passed parameter in lambda, in this case it’s upstream of Observable<String>).

This way we can convert rest of the flatMaps like following:

And we are done. Following is the final converted code:

I hope you like it and learned something new or may have revised it up. If you have any doubt and any suggestion, i would be happy to help you out, let me know in the comments or you can reach me out directly on twitter at @chandilsachin.

Special thanks to @drulabs for proof reading this article.

Please hit clap button and share to help it reach your friends in the community. You can follow me on medium to get updates on my upcoming articles.

My recent articles:

--

--