0

I have a Single.fromCallable() that I'm subscribed to. Using LiveData I'm trying to test UI error handling inside .subscribe()'s onError() callback method.

I've tried throwing Exceptions, calling disposable.dispose(), but I can't seem to fake an error so it directly goes to the onError() block.

 Single.fromCallable(() -> remoteRepository.doSomething())
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new SingleObserver<BigInteger>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        walletStatusLiveData.setValue("Now subscribed");

                        //How do I fake the error here so it jumps to onError() so I can test my error handling UI

                       throw new IllegalArgumentException(); //Crashes app
                       throw new RunTimeException(); //Crashes app

                       try {
                            throw new IllegalArgumentException();  //This trycatch doesn't do anything and onSuccess is called
                        }catch (Exception e){

                        }

                       d.dispose(); //This completely exits all 3 callbacks.
 
                    }

                    @Override
                    public void onSuccess(@NonNull BigInteger result) {
                        walletStatusLiveData.setValue("On success!");
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        Log.e(TAG, "onError: getWalletBalance Called", e);
                        walletStatusLiveData.setValue("Testing error UI");
                        //How do I trigger this callback on onSubscribe or onSuccess?
                    }
                });

What do I call to jump to .onError()?

DIRTY DAVE
  • 2,523
  • 2
  • 20
  • 83
  • 1
    The observable (in the case the callable) itself must throw an exception. You could mock out remoteRepository.doSomething to throw. Or make the observable a named class you can instantiate, and call onError directly. That may be cleaner, as you aren't trying to test that the observables call onError when an exception occurs- RxJava's tests test that. Of course if you directly access a bunch of member variables that may be a pain. – Gabe Sechan Jan 01 '22 at 06:33
  • ```in the case the callable) itself must throw an exception```. I didn't know you had to call it from there to trigger the onError callback. Thanks for the tips! – DIRTY DAVE Jan 01 '22 at 06:40
  • 1
    No problem. The reason for that is that onError is telling you the thing you're observing threw an error. That would be the callable. It allows you to react to that problem, for example react to a failure in a network call. If you were interested in exceptions in the observer, you'd just wrap it in a try/catch. – Gabe Sechan Jan 01 '22 at 06:43
  • If you'd like to write a quick answer I can accept it. I couldn't find any SO questions/answers on this – DIRTY DAVE Jan 01 '22 at 06:47
  • 1
    For testing purposes you can replace `Single.fromCallable(() -> remoteRepository.doSomething())` with `Single.error(new IllegalArgumentException()/* some exception */)`. `Single#error` invokes a subscriber's `onError` method when the subscriber subscribes to it. – Akaki Kapanadze Jan 01 '22 at 11:03
  • in 99% of cases, using `subscribe(Observer)` is an error. You want the ones with 1, 2 or 3 `Consumer`s. The `onSubscribe` is an internal-wiring method, it serves no purpose here other than confusion. Certainly, disposing the chain as it's created makes no sense. – Agent_L Jan 04 '22 at 15:29
  • @Agent_L Alot of the RxJava + Android tutorials are using ```.subscribe(Observer)```. https://medium.com/@DoorDash/synchronizing-network-calls-with-rxjava-d30f7db66fb9 https://medium.com/android-tutos/fetch-data-using-retrofit-and-rx-java-8dbb4de053b2 Can you give an example below without? – DIRTY DAVE Jan 04 '22 at 22:29
  • 1
    Write lambdas in place: `subscribe((result)->{ ; }, (exception) -> { ; })`. Like here: https://www.baeldung.com/rx-java Also, look into the code of `subscribe` yourself: "// can't call onError because no way to know if a Disposable has been set or not // can't call onSubscribe because the call might have set a Subscription already". This is the most critical method in Rx and exception there cannot be handled in a meaningful way. It's better to not put your code if you can't guarantee it will always succeed. – Agent_L Jan 05 '22 at 11:42
  • @Agent_L Thank you, ive been doing it wrong for a while now. Appreciate it. – DIRTY DAVE Jan 05 '22 at 18:13
  • My IDE throws ```Result of Single.subscribe() is ignored```. Is it necessary to dispose() of it or I can just ignore that lint? https://stackoverflow.com/a/47862794/11110509 This user asks a similar question and the accepted answer is implying that they just ignore the lint error. – DIRTY DAVE Jan 05 '22 at 18:47
  • 1
    @DIRTYDAVE The purpose of the return value is to stop the chain when you decide you don't want to receive values anymore. If you think of `subscribe` being analogue to `foreach` then `dispose` on it's return value is the analogue of `break`. The chain is auto-disposed when the Observable completes or errs, so if you're sure that you want to receive ALL values ever, then it's safe to ignore it. But if your receiver might live shorter than the source (which is most of the time), then save it and `dispose` when the receiver is disposed (eg. `onCleared` method of Android ViewModel). – Agent_L Jan 10 '22 at 10:16

0 Answers0