capt.swag
capt.swag

Reputation: 10661

Writing test cases for RxJava doOnSubscribe and doOnFinally situations

Let's say I've this particular piece of code in Kotlin. My question is how do I test during subscribing I have set the visibility of button to VISIBLE and set the text to "". And once subscription is completed I've made the visibility to GONE and test to "DONE".

fun getInfo() {
    getInfoFromApi()
        .doOnSubscribe {
            buttonVisibility.set(View.VISIBLE)
            buttonText.set(“”)
         }
        .onFinally {
            buttonVisibility(View.GONE)
            buttonText.set(“DONE”)
        }
        .subscribe {
            // Some other random stuff
        }
    }
}

As of now my test cases look like this.

@Test
fun getInfo() {
    viewModel.getInfo()
    Assert.assertEquals(View.GONE, viewModel.buttonVisibility.get())
    Assert.assertEquals(“DONE”, viewModel.buttonText.get())
}

Which obviously is correct when you look at what the final state should be, but how do I test the intermediate stages of doOnSubscribe?

Is there a way to Assert, if the button text was set to DONE from "", and visibility went from VISIBLE to GONE?

Upvotes: 1

Views: 783

Answers (1)

eleven
eleven

Reputation: 6857

doOnSubscribe and onFinally are just states of your View/ViewModel, so you can extract these states and check state changes:

fun getInfo() {
    getInfoFromApi()
        .doOnSubscribe {
            view.setButtonVisible()
         }
        .onFinally {
            view.setButtonGone()
        }
        .subscribe {
            // Some other random stuff
        }
    }
}

And then verify calls:

@Test
fun getInfo() {
    val view = mock(View::class)
    viewModel.getInfo()
    verify(view, times(1)).setButtonVisible())
    verify(view, times(1)).setButtonGone())
}

By the way, if your example supposed to be used with MVVM then your ViewModel should not have reference to View. ViewModel should provide bindings for view. And then code could look like this:

fun getInfo():Observable<State> {
    return concat(
       just(State.Progress), 
       getInfoFromApi().map(State.Result)
    ).onErrorResumeNext(just(State.Error))
}

And then tests became even more abstract and clear

@Test
fun getInfoSuccess() {
    //config api success behaviour
    TestObserver<State> observer = TestObserver.create();
    viewModel.getInfo().subscribe(observer)
    observer.assertResult(State.Progress, State.Result);
}

@Test
fun getInfoError() {
    //config api error behaviour
    TestObserver<State> observer = TestObserver.create();
    viewModel.getInfo().subscribe(observer)
    observer.assertResult(State.Progress, State.Error);
}

Upvotes: 1

Related Questions