Search code examples
javaunit-testingmockitorx-java2

How to mock an ObservableTransformer in unit tests using Mockito


In order to have reusable and testable rxjava code, I have separated parts of my code by using ObservableTransformers. It works fine in production, however testing it is not as easy as expected since I seem to be unable to mock those ObservableTransformers.

This is the class to test:

import io.reactivex.Observable;
import io.reactivex.ObservableTransformer;
import io.reactivex.subjects.PublishSubject;

public class Cut {

    private PublishSubject<String> emitter = PublishSubject.create();
    private ITransformerProvider transformerProvider;

    public Cut(ITransformerProvider transformerProvider) {
        this.transformerProvider = transformerProvider;
    }

    public void writeNewText(String text){
        emitter.onNext(text);
    }

    public Observable<String> getActualText(){
        return emitter
                .compose(transformerProvider.getObservableTransformer());
    }

    class MyActualObservableTransformer implements ITransformerProvider {
        @Override
        public ObservableTransformer<String, String> getObservableTransformer() {
            //does something I do not want to execute in unit test, e.g. REST call
            return null;
        }
    }
}
import io.reactivex.ObservableTransformer;

public interface ITransformerProvider {
    ObservableTransformer<String, String> getObservableTransformer();
}

Lets assume the productive MyActualObservableTransformer needs mocking in order to have a plain unit test.

And this is a simple test attempting to test what happens when I hand over a new text. I do realize the test makes no sense, it is just condensed to show my problem:

public class TextTest {

    @Mock
    private ITransformerProvider transformerProvider;
    @Mock
    private ObservableTransformer<String, String> observableTransformer;

    private TestObserver<String> testObserver;
    private Cut cut;


    @Before
    public void setup(){
        MockitoAnnotations.initMocks(this);

        when(transformerProvider.getObservableTransformer()).thenReturn(observableTransformer);
        when(observableTransformer.apply(any())).thenReturn(Observable.just("mockedText"));

        testObserver = new TestObserver<>();
        cut = new Cut(transformerProvider);

        cut.getActualText()
            .observeOn(Schedulers.trampoline())
            .subscribe(
                    newText -> testObserver.onNext(newText)
                    , error -> Assert.fail(error.getMessage()));
    }

    @Test
    public void testGetActualText(){
        cut.writeNewText("ignoredText");
        testObserver.assertValueAt(0, text -> text.equals("mockedText"));
    }
}

The problem is that when running the test, the emitter has no subscribers.

Having analyzed the call stack I think I know what the problem is. So as far as I understand it, when subscribe is called, all subscribes of the observables within the chain are called. Without my ObservableTransformer mock, this is what happens and this is why it works fine in production.

However, when I add my mock:

when(observableTransformer.apply(any())).thenReturn(Observable.just("mockedText"));

as soon as subscribe for it is called, it emits a new value and the subscription chain is never completed. So my question is, how can I mock an observable without it emitting a value during subscription? I guess Observable.just or a BehaviorSubject is not the way to go since it behaves as expected in this case, as far as my still limited RxJava knowledge goes. Any ideas?


Solution

  • The simplest solution I came up with is to just not use Mockito for mocking and mock it myself:

    private ObservableTransformer observableTransformer = text -> text.map(ignore -> "mockedText");