Android - How to debug callback within RxJava Subscription - android

I'm trying to debug the inner success callback of an RxJava Subscription object and verify certain methods inside that get called with the correct argument(s). I read up some about Captors and though maybe that's the way to go but was unable to find an example that matched my needs.
I'm unit testing with: Mockito and JUnit
// OrderHistoryPresenterImpl presenter;
public void loadOrderHistory(final int offset, final int limit) {
mCompositeSubscription.add(useCase.getOrderHistory(offset, limit)
.doOnError(throwable -> Timber.e(throwable, "Error"))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ListOfOrders>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(ListOfOrders listOfOrders) {
//
// I would like to verify the calls in this method
//
user.setTryoutCountActive(listOfOrders.getActiveTryoutsCount());
getView().addOrdersToView(listOfOrders);
mCompositeSubscription.clear();
}
})
);
}
And here are the tests I'm trying to test it with
#RunWith(MockitoJUnitRunner.class)
public class OrderHistoryPresenterTest {
// Boiler plate Dagger 2 setup + variable declarations
private ListOfOrders response;
private ListOfOrders spyListOfOrders;
private OrderHistoryPresenterImpl presenter;
#Mock private OrderHistoryUseCaseImpl useCase;
#Before
public void setUp() throws FileNotFoundException {
// Setup + dagger 2 setup…
response = Utils.fromJson(gson, this, "OrderHistory.json", type);
spyListOfOrders = spy(response);
presenter = new OrderHistoryPresenterImpl(app, useCase, user);
when(useCase.getOrderHistory(MOCK_USER_ACCESS_TOKEN)).thenReturn(Observable.just(spyListOfOrders));
}
#After
public void tearDown() {…}
#Test
public void shouldGetOrderAndCall_addOrdersToView_OnActivity() {
presenter.loadOrderHistory(MOCK_OFFSET, MOCK_LIMIT);
// works…
verify(useCase, times(1)).getOrderHistory(MOCK_USER_ACCESS_TOKEN);
// This fails because it gets called right away because it's async - what to do?
verify(view, times(1)).addOrdersToView(spyListOfOrders);
}
}
So as you can see I don't know how to test the inner Subscriber<ListOfOrders> callback methods. Any help would be greatly appreciated!

Let's say if you rewrite your method like this,
// OrderHistoryPresenterImpl presenter;
public void loadOrderHistory(final int offset, final int limit) {
mCompositeSubscription.add(useCase.getOrderHistory(offset, limit)
.doOnError(throwable -> Timber.e(throwable, "Error"))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ListOfOrders>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(ListOfOrders listOfOrders) {
load(listOfOrders)
}
})
);
}
void loadOrders(ListOfOrders list) {
// I would like to verify the calls in this method
user.setTryoutCountActive(listOfOrders.getActiveTryoutsCount());
getView().addOrdersToView(listOfOrders);
mCompositeSubscription.clear();
}
Now test this loadOrders method separately. Because all it cares about is getting a list of orders to process. It can come from anywhere (not only observable).
You can unit test this method now.
If you want to test Observable, then use TestSubscriber to see if you are actually getting a list of orders from Observable.
Ref : https://reactivex.io/RxJava/javadoc/rx/observers/TestSubscriber.html‌​

Related

Creating doIfEmpty operator in RxJava

I have 2 streams, the first stream is a stream which takes data from database and call onCompleted() after finish taking data. The second stream is a stream that takes live data from server and never call onCompleted(). What I want to do is to create an operator that can do an action if the first stream(upstream) is an empty stream. Here is the sample:
getItemFromDatabase()
.lift(new DoIfEmptyOperator<Item>(new Action0() {
#Override
public void call() {
//Database is empty
System.out.println("Yeay successfully do an action");
}
}))
.concatWith(getItemFromServer()) // -----> intentionally never complete
.subscribe(new Subscriber<StoryItem>() {
#Override
public void onCompleted() {
//dosomething...
}
#Override
public void onError(Throwable e) {
//dosomething...
}
#Override
public void onNext(StoryItem storyItem) {
//dosomething
}
}));
Here is the code of DoIfEmptyOperator:
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
public class DoIfEmptyOperator<T> implements Observable.Operator<T,T>{
private Action0 action;
private boolean isEmpty = true;
public DoIfEmptyOperator(Action0 action) {
this.action = action;
}
#Override
public Subscriber<? super T> call(final Subscriber<? super T> childSubscriber) {
Subscriber<T> parentSubscriber = new Subscriber<T>() {
#Override
public void onCompleted() {
if(isEmpty) {
action.call();
}
childSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
childSubscriber.onError(e);
}
#Override
public void onNext(T t) {
isEmpty = false;
childSubscriber.onNext(t);
}
};
childSubscriber.add(parentSubscriber);
return parentSubscriber;
}
}
However the action is never executed because the parentSubscriber onCompleted() is not firing, because the downstream never completed. If I remove
.concatWith(getItemFromServer())
then the action is executed. Any clue about how to solve the problem? I have dived to the source code of Observable.switchIfEmpty() but still have no clue about how it works.
I would advise against creating an operator.
This could be easily done with existing operators like this:
getItemFromDatabase()
.toList()
.flatMap(list -> {
if (list.isEmpty()) {
// side effect here
}
return getItemFromServer();
});
Have you thought about switchIfEmpty()? As an example of the usage of this operator - I have created some code on GitHub at the following link:
https://github.com/rs146/rxjava-simple/blob/master/src/test/java/SwitchIfEmpty.java
switchIfEmpty() is called when no items are emitted.
However, if you want to get items from the api or the db, then you can do something like the following:
Observable.concat(getFromDatabase(), getFromApi()).first();
As long as both getFromDatabase() and getFromApi() return the same Observable Type. This is a common Rx idiom in Android apps. It basically states that if an item's is not emitted from the database, then go fetch the result from the API instead.

RxAndroid not returning Subcription for the observer

I have started learning RxAndroid and below is the code I wrote to iterate over a model object (Results) that contains data fetched from the server. I'm iterating over the model object in the observable and providing a newly created object in the observer. I'm trying to take subscription of the observer to unsubscribe the task upon Orientation changes of the fragment. However the subscribe() returns VOID instead of subscription object.
Questions:
Does the latest version of RxAndroid handle unsubscription itself upon configuration/orientation change?
In case configuration change happens before the task is complete, the only way to restart this task that I can think of is, I persist the server response in onSavedInstance() and retrieve it from bundle when the fragment is recreated. It'll require booleans to figure out if the configuration change happened before the configuration change or not. Is there a graceful and cleaner way of coping with this?
private void createComicList(final List<Result> marvelResults) {
final MarvelComics marvelComics = new MarvelComics();
Observable marvelObservable2 = Observable.create(new ObservableOnSubscribe<MarvelComic>() {
#Override
public void subscribe(ObservableEmitter<MarvelComic> e) throws Exception {
for(Result result : marvelResults) {
MarvelComic marvelComic = new MarvelComic();
marvelComic.setDescription(result.getDescription());
marvelComic.setTitle(result.getTitle());
marvelComic.setPageCount(result.getPageCount());
marvelComic.setThumbnailUrl(result.getThumbnail().getPath());
marvelComic.setId(result.getId());
e.onNext(marvelComic);
}
e.onComplete();
}
});
marvelObservable2.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MarvelComic>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(MarvelComic comic) {
marvelComics.getMarvelComicList().add(comic);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
showToast();
}
});
}
The Observable.subscribe(Observer<? super T>) method returns void in the 2.x since the Observer.onSubscribe(Disposable) is there to get the cancellation support that used to be Subscription in 1.x.
final CompositeDisposable composite = new CompositeDisposable();
Observable<Integer> source = Observable.just(1)
source.subscribe(new Observer<Integer>() {
#Override public void onSubscribe(Disposable d) {
composite.add(d); // <---------------------------------------------
}
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
composite.add(source
.subscribeWith( // <-----------------------------------------------
new DisposableObserver<Integer>() {
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
subscribe() method of Observable returns Subscription object in earlier versions of RxJava and current version returns an object of Disposble class which you can unsubscribe by invoking dispose() method.
For your second question you may check this answer Best practice: AsyncTask during orientation change

How to return value in rxJava

I'm new into rxJava and it's making my head spin. Basically I'm pulling data from youtube api with retrofit which gives back Observable and with youtubeDataMapper I'm mappng it into Youtube Pojo object which contains String videoID. So my question is, how to make this method return that string instead of Completable?
This is my method:
#Override
public Completable downloadVideoUrl(String query) {
addSubscription(youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.subscribe());
return Completable.complete();
}
You have two choices:
Make your downloadVideoUrl return Observable instead of Completable:
Preferred way:
#Override
public Completable downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler);
}
Notice lack of subscribe operator here.
Then wherever you want to get videoId:
downloadVideoUrl(query)
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String videoId) {
// do whatever you want with videoId
}
});
Use toBlocking().first()
This is not preffered as you block current Thread until Observable finishes
#Override
public String downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.toBlocking().first();
}
First of all, it is better to make Retrofit return Single instead of Observable because you are expecting a single server response (and not a sequence of responses).
Secondly, Completable.complete() is a factory method for a Completable that does nothing at all. So you don’t need it here.
Regarding String videoID, it depends on what you are planning to do with it. Also, I have no idea what your .addSubscription() is doing.
I would suggest doing something like the following:
class YourClass {
private final CompositeSubscription compositeSubscription = new CompositeSubscription();
// you must call compositeSubscription.clear() either in class .finalize() or on some UI lifecycle event
void yourMethod() {
final Single videoID = youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
final Subscription subscription = videoID.subscribe(new SingleSubscriber() { 
 #Override
 public void onSuccess(String value) {
// TODO: do whatever with the value
}
#Override
public void onError(Throwable error) {
// TODO: log and/or display error
}
});
compositeSubscription.add(subscription);
}
}

Android retrofit2 with rx sequential requests

I'm using retrofit2 with Rx. I have two API calls. If first call returns empty response with code 400 I need to make second API call, if not then just to show result. I've implemented custom error handling how shown here. Here is my solution:
getResponse1(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(Response1 response) {
view.onSuccess(response);
}
#Override
public void onClientError(BaseError response) {
getResponse2(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response2, BaseError>(BaseError.class) {
#Override
public void onNext(Response2 response) {
view.onSuccess(response);
view.hideProgress();
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Is it possible to simplify this code that goes inside method onClientError? Is it good solution to like that?
1). To simplify it, it would be better that Response1 and Response2 will extend some base class, so in your chain, you can operate with base class, which can be casted to certain type when needed
So, let's assume, that you have BaseResponse:
public abstract class BaseResponse{
public static int TYPE_RESPONSE_1 = 1;
public static int TYPE_RESPONSE_2 = 2;
public abstract int getType(); //every class MUST override this method
}
Response1 and Response2 should override BaseResponse
2). getResponse1 and getResponse2 should return Observable<BaseResponse>
3). Your target code:
getResponse1(token)
.onErrorResumeNext(new Func1<Throwable, Observable<BaseResponse>>() {
#Override
public Observable<BaseResponse> call(Throwable throwable) {
// I use Retrofit 1.9
// And in Retrofit 1.9 I have class RetrofitError, which may provide me all info about error
// I'm absolutelly sure Retrofit 2 also have such class,
// but it may have another name
if(/* is your target error */){
//cast it tour target error
return getResponse2(token);
}
return Observable.error(throwable);
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(BaseResponse response) {
if(response.getType() == BaseResponse.TYPE_RESPONSE_1){
view.onSuccess(response);
} else {
view.onSuccess(response);
view.hideProgress();
}
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Jeesh, why does everyone make it so complicated? In practice I've felt that every time I needed to subscribe to an Observable in the subscribe of another, there is an operator that will do it much more cleanly for me:
<T,E extends Throwable> Observable<T>
whenExceptionIs(Class<E> what, Func1<E,Observable<T>> result) {
return t -> {
return what.isInstance(t) ? result.call(t) : Observable.error(t);
};
}
getResponse1(token)
.onErrorResumeNext(whenExceptionIs(BaseError.class, getResponse2(token)))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(view::onSuccess, view::hideProgress, err -> view.hideProgress());
If you have special error handling needs, create a custom Subscriber that handles that, but make sure that error handling in general is
handled in the Observable chain if it can do anything about it (ignore it, retry the calls etc)
propagated downstream.

rxAndroid retrofit get result of each call from the sequence

I have to execute 3 API calls in the sequence and to do so
I use observable.concatMap(new Func1<>...)
and at the last one I have a subscriber to change activity
However I want to update progressBar in UI thread to let user know that part of task is done.
private void getAllData() {
updateUserTask(
getUserScheduleObservable(
getCurrentUserObservable()));
}
private void updateUserTask(Observable<TaskWrapper> observable) {
wrapObservable(observable)
.subscribe(new Subscriber<TaskWrapper>() {
#Override
public void onNext(TaskWrapper taskWrapper) {
openCurrentFragment();
hideProgressIndicators();
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
});
}
private Observable<TaskWrapper> getUserScheduleObservable(Observable<ScheduleWrapper> observable) {
return observable.concatMap(
scheduleWrappers1 -> apiManager.getRouteObservable(vehicleDeliveryAreaRiderBundle.getVehicle().getId()));
}
private Observable<ScheduleWrapper> getCurrentUserObservable() {
return apiManager.getUserObservable().concatMap(
user -> apiManager.getCurrentScheduleObservable()
);
}
I think that you are looking for something like this.
public class ExampleUnitTest {
#Test
public void testSample() throws Exception {
Observable<String> first = Observable.just("First");
Observable<String> second = Observable.just("Second");
Observable<String> third = Observable.just("Third");
Observable.concat(first, second, third)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(this::updateProgress)
.subscribe();
}
private void updateProgress(String s) {
System.out.println(String.format("Notify your progress that %s ended", s));
}
}
Just concatenating those observables, you can achieve the expected result.
Hope that it helps.
Best regards.

Categories

Resources