NullPointer
NullPointer

Reputation: 1329

How to test complex RxJava Observables

I am using RxJava in my android project and I'm actually starting unit testing my code.

I have following Observable method which I'm going to test

private Observable<Boolean> changeMyString (@NonNull String testString) {
    return OneClass
        .doSth1(testString)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap(testString2 -> Observable.create(
            subscriber -> {
                manager.setTestString2(testString2);
                AnotherClass
                    .doSth2(testString2)
                    .subscribe(
                        testString3 -> {
                            manager.setTestString3(testString3);
                            subscriber.onNext(success);
                        },
                        subscriber::onError,
                        subscriber::onCompleted
                    );
            }
        ));

OneClass.java doSth1(String testString)

private Observable<Object> doSth1 (@NonNull String testString) {
    return Observable.create(subscriber -> {
        try {
            testString = "CHANGEDSTRING"
            subscriber.onNext(testString);
            subscriber.onCompleted();
        }
        catch (Exception e) {
            subscriber.onError(e);
        }
    });
}

AnotherClass.java doSth2(Object someObject)

    public Observable<String> doSth2 (@NonNull Object someObject) {
    return Observable.create(subscriber -> {
        String changeText = preferences.getString(ANYSTRING, null); //preferences = SharedPrefences
        if (StringHelper.isEmpty(changeText)) {
            subscriber.onCompleted();
            return;
        }
        try {
            String finishedString = "WE ARE DONE! " + someObject.toString();
            subscriber.onNext(finishedString);
            subscriber.onCompleted();
        }
        catch (Exception e) {
            subscriber.onError(e);
        }
    });
}

My Test case looks like the following:

TestSubscriber<Boolean> testSubscriber = new TestSubscriber<>();
ClassToTest.changeMyString("TESTSTRING").subscribe(testSubscriber);

When I debug my Code it's not reaching this part here:

...
.flatMap(testString2 -> Observable.create(
        subscriber -> {
            manager.setTestString2(testString2);
            AnotherClass
                .doSth2(testString2)
                .subscribe(
                    testString3 -> {
                        manager.setTestString3(testString3);
                        subscriber.onNext(success);
                    },
                    subscriber::onError,
                    subscriber::onCompleted
                );
        }
    ));

How can I write a Unit test for this case? I can't figure it out, why it's not working...

Thanks for your help guys!

Alban

Upvotes: 0

Views: 1256

Answers (2)

cambierr
cambierr

Reputation: 421

Creating a new Observable stream inside a onSubscribe is generally a bad idea.

You should consider something like this:

    OneClass
    .doSth1(testString)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .doOnNext(testString2 -> manager.setTestString2(testString2))
    .flatMap(testString2 -> AnotherClass.doSth2(testString2))
    .doOnNext(testString3 -> manager.setTestString3(testString3))
    .map(setTestString3 -> true);

From what I see in your code, this should give you what you want.

Upvotes: 1

dwursteisen
dwursteisen

Reputation: 11515

Instead of using directly Schedulers, you may want to inject it and use TestScheduler instead. It will allow your to controle executions. (as for an unit test, it may be complicated to deal with others schedulers)

For more information on the TestScheduler : How to use TestScheduler in RxJava

 class YourClass {

        private final Scheduler ioScheduler;
        private final Scheduler uiScheduler;

        public YourClass(Scheduler ioScheduler, Scheduler uiScheduler) {
               this.ioScheduler = ioScheduler;
               this.uiScheduler = uiScheduler;
        }

        private Observable<Boolean> changeMyString (@NonNull String testString) {
         return OneClass.doSth1(testString)
                        .subscribeOn(ioScheduler)
                        .observeOn(uiScheduler)
                        .doOnNext(str -> manager.setTestString2(str))
                        .flatMap(testString2 -> AnotherClass
                        .doSth2(testString2))
                        .doOnNext(str -> manager.setString3(str));
         }
 }

Then in your test, you can inject a TestScheduler instead :

   @Test
   public void example() {
          TestScheduler ui = new TestScheduler();
          TestScheduler io = new TestScheduler();
         YourClass hello = new YourClass(io, ui);
           TestSubscriber subscriber = new TestSubscriber();
           hello.changeMyString("stuff").subscribe(subscriber); 

         io.advanceByTime(2, SECONDS);
         ui.advanceByTime(2, SECONDS);

         // perform check on subscriber here
    }

Please note that I refactored your code as you should avoid nested subscription.

Upvotes: 2

Related Questions