Emitting test events with RX: merge/zip/interval

Manually testing features based on Observables on a real device during the implementation phase can be tricky. However today I found a really simple trick to do just that.

In the following example I’ll be using Kotlin on Android and RxJava, but the concept and RX operators should be applicable to all RX compatible languages.

Imagine you have an Observable which occasionally emits state changes based on user interactions with your app. For our example we’ll look at changes of the user name. The user name is part of a few UI components of our app so an Observable emitting user name changes might be handy.

Let’s say we already have such an Observable<String> (internally this could be a BehaviorSubject) provided by a repository class. While writing a new UI component (e.g. a sidebar) which contains a TextView showing the user name we want to quickly test what happens when the state changes multiple times without having to manually navigate to the Activity which provides the UI to change the name.

Consider the subscription to the name state Observable looks like this:

class SidebarPresenter(val userInfoRepository: UserInfoRepository) {

  fun init() {
    ...
    userInfoRepository
      .getNameStateObservable()
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe { userName -> userNameTextView.setText(userName) }
    ...
  }

}

Nothing fancy here.

As already outlined before we could test this on the device by switching to an imaginary UserSettingsActivity, change back to the main activity, open the side bar, check whether the name has changed, change back to the settings activity, change the name to something really long or with awkward symbols, change back to the main activity and so on, until we’ve covered all important cases. Later on we’ll probably write some Espresso tests to check this automatically, but writing UI tests in the implementation phase can be tedious.

So how can we quickly check those different cases? RX’s merge(), zip() and interval() can help us a great deal here! Look at this:

class SidebarPresenter(val userInfoRepository: UserInfoRepository) {

  fun init() {
    ...
    userInfoRepository
      .getNameStateObservable()
      .mergeWith(
        Observable.from(arrayOf("Arthur Dent", "Genghis Temüjin Khan", "Know-Nothing Bozo the Non-Wonder Dog", ""))
          .zipWith(
            Observable.interval(5, TimeUnit.SECONDS), 
            { name, _ -> name }
          )
      )
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe { userName -> userNameTextView.setText(userName) }
    ...
  }

}

I’ve split this up a bit so that we can go through it line-by-line.

First we’ve introduced the [mergeWith()](http://reactivex.io/documentation/operators/merge.html) operator. This allows merging two streams to one, emitting items in the order they arrive no matter which stream they originate from. The stream we’ll merge our name state Observable with is the one generating our test events.

So what is this test generator stream composed of? First of all we have a simple Observable generated from an array of Strings containing various possible user names (line 8). That’s the easy part.

We then [zipWith()](http://reactivex.io/documentation/operators/zip.html) the test name Observable with an [interval()](http://reactivex.io/documentation/operators/interval.html) Observable. The interval Observable, as defined in line 10, “emits a sequential number every specified interval of time” (as per its Javadoc). In our case we want such a number to be emitted every 5 seconds. This will be our clock generator.

The zipWith() operator also needs a zip function which describes how the two streams are zipped —here we don’t care for the values emitted by the interval Observable, we just need the ticks: { name, _ -> name } (line 11). To make sense of that please take a look at the zip documentation and play around with the embedded marble diagram — it does a much better job at explaining how this works than I could ever hope to do.

When we now run our app on our device and open the sidebar we’ll see the user name TextView changing its content every 5 seconds to the defined values making it easy and quick to test and fix various combinations.

Of course this isn’t just for testing UI related stuff, but can be applied to a multitude of RX related use cases. The “merge/zip/interval” trick saved me a great deal of time once I got it working. Do you use similar RX implementation phase tricks? Let me know in the comments!

Did you find this article valuable?

Support Sven Bendel by becoming a sponsor. Any amount is appreciated!