I am testing real API Calls with Retrofit as following:
#Test
public void getList(){
TestObserver<MyResponse> testObserver = api
.getResults(params)
.lift(apiErrorOperator)
.lift(emptyResponseOperator)
.test();
testObserver.awaitTerminalEvent();
testObserver.assertError(ApiException.class);
}
The test fails with these 2 errors:
Caused by: java.lang.IllegalStateException: onSubscribe not called in proper order
and
Caused by: com.example.myapplication.repository.ApiException: Search found 0 results
The second makes sense, since this is the behaviour I am expecting. However, I do not understand why testObserver.assertError(ApiException.class) is not returning true, and why I get the first error too.
For the first error, this line java.lang.IllegalStateException: onSubscribe not called in proper order is thrown at this line observer.onError(new ApiException("Search found 0 results")) from emptyResponseOperator. Below is code for full class:
public class EmptyResponseOperator implements ObservableOperator<MyResponse, MyResponse> {
#Override
public Observer<? super MyResponse> apply(Observer<? super MyResponse> observer) throws Exception {
return new DisposableObserver<MyResponse>() {
#Override
public void onNext(MyResponse myResponse) {
if(myResponse.getTotalResultsCount() == 0)
observer.onError(new ApiException("Search found 0 results"));
else{
observer.onNext(myResponse);
observer.onComplete();
}
}
#Override
public void onError(Throwable e) {
observer.onError(e);
}
#Override
public void onComplete() {
observer.onComplete();
}
};
}
}
And also here is the code for ApiErrorOperator class:
public class ApiErrorOperator<T> implements ObservableOperator<T, Response<T>> {
#Override
public Observer<? super Response<T>> apply(Observer<? super T> observer) throws Exception {
return new DisposableObserver<Response<T>>() {
#Override
public void onNext(Response<T> tResponse) {
if(!tResponse.isSuccessful()){
try {
if (tResponse.errorBody() != null) {
observer.onError(new ApiException(tResponse.errorBody().string()));
}else{
observer.onError(new ApiException(C.ERROR_UNKNOWN));
}
} catch (IOException e) {
observer.onError(new ApiException(C.ERROR_IO));
}
}
else if (tResponse.body() == null) {
observer.onError(new ApiException(C.ERROR_NOT_FOUND));
}else{
observer.onNext(tResponse.body());
observer.onComplete();
}
}
#Override
public void onError(Throwable e) {
observer.onError(e);
}
#Override
public void onComplete() {
observer.onComplete();
}
};
}
}
We don't recommend writing custom behavior this way. You have to follow the Observable protocol, like this:
public class EmptyResponseOperator implements ObservableOperator<MyResponse, MyResponse> {
#Override
public Observer<? super MyResponse> apply(Observer<? super MyResponse> observer)
throws Exception {
return new DisposableObserver<MyResponse>() {
// -------------------------------------
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#Override
public void onStart() {
observer.onSubscribe(this);
}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// -------------------------------------
#Override
public void onNext(MyResponse myResponse) {
dispose(); // <-------------------------------------------------------
if (myResponse.getTotalResultsCount() == 0) {
observer.onError(new ApiException("Search found 0 results"));
} else {
observer.onNext(myResponse);
observer.onComplete();
}
}
#Override
public void onError(Throwable e) {
if (!isDisposed()) { // <---------------------------------------
observer.onError(e);
}
}
#Override
public void onComplete() {
if (!isDisposed()) { // <---------------------------------------
observer.onComplete();
}
}
};
}
}
Your implementation is wrong, and try to avoid chain travels downstream in your code.
check below sample and go through documentation here.
Single.just(1)
.delaySubscription(Completable.create(new CompletableOnSubscribe() {
#Override
public void subscribe(CompletableEmitter e) throws Exception {
if (!e.isDisposed()) {
e.onError(new TestException());
}
}
}))
.test()
.assertFailure(TestException.class);
-- onSubscribe wires them up and there you go.
Another solution
create custom operator. How?
Related
Hi I am quite naive with rxJava, so please let me know if my understanding is correct. As per my understanding if I use backpressure strategy with LATEST flag, I should be getting the most recent value. So I have a list, I am using flowable with subscriber, but it is still printing all values in onNext . Also please let me know how to test this backpressure strategy as I do not have real time data.
Following is my code
Observable.just(listObj).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).
flatMap(new Function<List<String>, Observable<String>>() {
#Override
public Observable<String> apply(List<String> ints) {
return Observable.fromIterable(ints);
}
}).toFlowable(BackpressureStrategy.LATEST).subscribe(new Subscriber<String>() {
#Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE);
}
#Override
public void onNext(String s) {
Log.d("Value", s);
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
}
});
}
///////////
Following is s simple method which creates arraylist
private ArrayList<String> createListForReturn() {
try {
if (listObj != null) {
listObj.add("Thanks");
listObj.add("fine");
listObj.add("working");
listObj.add("is");
listObj.add("Flowable");
listObj.add("Now");
listObj.add("Flowable");
listObj.add("For");
listObj.add("Programme");
listObj.add("RxJava");
listObj.add("Second");
listObj.add("My");
listObj.add("is");
listObj.add("This");
listObj.add(" ");
}
return listObj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I am using rxjava 2 and trying to use rxbus for passing a value
rxbus code
public class SeasonTabSelectorBus {
private static SeasonTabSelectorBus instance;
private PublishSubject<Object> subject = PublishSubject.create();
public static SeasonTabSelectorBus instanceOf() {
if (instance == null) {
instance = new SeasonTabSelectorBus();
}
return instance;
}
public void setTab(Object object) {
try {
subject.onNext(object);
subject.onComplete();
} catch (Exception e) {
e.printStackTrace();
}
}
public Observable<Object> getSelectedTab() {
return subject;
}
}
I am setting the value as
SeasonTabSelectorBus.instanceOf().setTab(20);
This is code of my subscription
SeasonTabSelectorBus.instanceOf().getSelectedTab().subscribe(new Observer<Object>(){
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
if (o instanceof Integer) {
int seasonSelected =(int) o;
Log.e("season selected",seasonSelected+"");
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Now I am getting the value on the first call, but when I call again with different values, I do not get the callback.
SeasonTabSelectorBus.instanceOf().setTab(40);
SeasonTabSelectorBus.instanceOf().setTab(90);
SeasonTabSelectorBus.instanceOf().setTab(120);
SeasonTabSelectorBus.instanceOf().setTab(290);
You are receiving only the first one because, after publish (subject.onNext(object)), you are calling subject.onComplete(). Just remove that line.
I want to test a function on the Android presenter that have a callback on it. This is the function:
public void findRandomUsers() {
view.showProgress();
mDataManager.getRandomUsers(USERS_SEARCH_NUMBER, new Callback<UserList>() {
#Override
public void onResponse(Call<UserList> call, Response<UserList> response) {
if(view == null) return;
view.hideProgress();
if(response.body().getUsers().isEmpty()){
view.showIsEmptyError();
}
users = response.body();
users.setUsers(CheckRemovedUsers.avoidRemoveds(users.getUsers(), removedUsers.getRemovedUsers()));
users.setUsers(CheckDuplicatedUsers.removeDuplicated(users.getUsers()));
if(isFirstTime)
view.showUsersList(users);
else
view.updateUserList(users);
}
#Override
public void onFailure(Call<UserList> call, Throwable throwable) {
if(view == null) return;
view.hideProgress();
view.showError(throwable.getMessage());
}
});
}
The Callback is a retrofit2.Callback object USERS_SEARCH_NUMBER is an int object. If it is possible I want to control what the callback response to control if when it returns an empty response or it fails it shows the correct answer.
You have to design your achitecture the way, that you have enough seams.
Currently, you are lacking to mock the Callback<UserList>. Why won't you have
a Factory class, which provides you that callback? Then you can easily stub components.
class UserListCallbackFactory {
public UserListCallbackFactory() {}
public Callback<UserList> getCallback(Presenter presenter) {
return new Callback<UserList>() {
#Override
public void onResponse(Call<UserList> call, Response<UserList> response) {
presenter.onSuccess(response.body().getUsers());
}
#Override
public void onFailure(Call<UserList> call, Throwable throwable) {
presenter.onFailure(throwable);
}
}
}
}
Now, in the constructor of your presenter:
class Presenter extends ... {
Callback<UserList> userListCallback;
DataManager dataManager;
public Presenter(View view, UserListCallbackFactory factory, DataManager dataManager) {
...
userListCallback = factory.getCallback(this, view);
this.dataManager = dataManager;
...
}
public void onSuccess(List<User>) {
...
}
public void onFailure(Throwable e) {
...
}
}
Now you have enough seams to mock your callback in your unit test class.
#RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
...
#Mock View view;
#Mock Callback<UserList> userListCallback;
#Mock UserListCallbackFactory factory;
#Mock DataManager dataManager;
#InjectMocks
Presenter presenter;
...
#Test
public void succesfulResponse() {
when(factory.getCallback(presenter)).thenReturn(userListCallback);
when(dataManager.getRandomUsers(USERS_SEARCH_NUMBER, userListCallback))
.thenAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
userListCallback.onSuccess(SOME_LIST);
return null;
}
});
// check that appropriate actions are performed upon successful callback
}
}
I've an observable like this
Observable.zip(observable, extObs, new Func2<List<UserProfile>, ArrayList<Extension>, UserProfile>() {
#Override
public UserProfile call(List<UserProfile> userProfiles, ArrayList<Extension> extensions) {
return userProfiles.get(0);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).unsubscribeOn(Schedulers.io()).subscribe(new Subscriber<UserProfile>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(UserProfile userProfile) {
profileListener.onProfileSet(userProfile);
}
});
}
I need to pass the ArrayList in the methodprofileListener.onProfileSet(userProfile); as profileListener.onProfileSet(userProfile,extensions);
Is it possible to do so in zip or is there any other methods of rxjava to solve such type of problems?
You have to do exactly what cricket_007 suggested in the comment.
For example like this:
Create a class that would represent combined results of your observables:
class CombinedResults {
public UserProfile userProfile;
public List<Extension> extensions;
public CombinedResults(UserProfile userProfile, List<Extension> extensions) {
this.userProfile = userProfile;
this.extensions = extensions;
}
}
(Alternatively you could use Pair class)
Use an object of CombinedResults (or Pair) in your Observable.zip Func2.
Observable.zip(observable, extObs,
new Func2<List<UserProfile>, ArrayList<Extension>, CombinedResults>() {
#Override
public CombinedResults call(List<UserProfile> userProfiles, ArrayList<Extension> extensions) {
return new CombinedResults(userProfiles.get(0), extensions);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<CombinedResults>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(CombinedResults combined) {
profileListener.onProfileSet(combined.userProfile, combined.extensions);
}
});
I'm looking a way to define order(?) of observers.
#GET("/get_user_msgs")
Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params);
For example I gave a Observable from my Rest API created by Retrofit.
In my ListView I'm observing this Observable.
api.getPrivateMessages(params).subscribe(new Observer());
I also have an API wrapper for my Espresso tests and I'm subscribing to same Observable there. This way observer in API wrapper is called first and only then observer in ListView
is called.
public class IdlingWrapper implements Api, IdlingResource {
....
public IdlingWrapper(Api realApi) {
this.realApi = realApi;
}
...
public Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params); {
counter.incrementAndGet();
return wrapObservable(realApi.getPrivateMessages(params));
}
protected <T> Observable<T> wrapObservable(final Observable<PrivateMessagesResponse> observable) {
//what to do here?
}
}
Is there a way to force some observer to be notified after all others are done? Or something similar in that matter?
Something like
Observable observable = getObservable();
observable.subscribeAsLast(new LastObserver());
observable.subscribe(new ObserverA());
observable.subscribe(new ObserverB());
And so that ObserverA would be notified first, then ObserverB and only then LastObserver.
Or any other approach where I could find out when all registered observers were notified and completed.
I'm not exactly sure what you are trying to do in IdlingWrapper, but I think the current implementation is very fragile.
I think the most important thing that needs to happen is to guarantee the observable can only be called once.
Here is a quick implementation to demonstrate that as well as my implementation of wrapObservable.
public class Test {
private static int counter = 0;
private static final List<Observable<?>> list = Collections.synchronizedList(new ArrayList<>());
protected static <T> Observable<T> wrapObservable(final Observable<T> original) {
// run atleast once???
synchronized (list) {
list.add(original);
}
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
synchronized (list) {
counter++;
if (!list.contains(original)) {
subscriber.onError(new Exception("You can only subscribe once!"));
return;
}
list.remove(original);
}
// Sleep to make it easier to see things happening...
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
subscriber.onCompleted();
}
}).flatMap(new Func1<Void, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Void o) {
return original;
}
}).finallyDo(new Action0() {
#Override
public void call() {
synchronized (list) {
counter--;
if (list.size() == 0 && counter == 0) {
System.err.println("finally");
}
}
}
});
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
// running in io thread for simulating async call.
Observable<String> test = wrapObservable(Observable.from("TEST!!!!!!")).subscribeOn(Schedulers.io());
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
// example of calling the same observable twice.
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
}
Thread.sleep(10000);
}
}
It seems, that this worked just fine.
protected <T> Observable<T> wrapObservable(final Observable<T> original) {
return Observable.create(new Observable.OnSubscribeFunc<T>() {
#Override
public Subscription onSubscribe(final Observer<? super T> t1) {
original.subscribe(new Observer<T>() {
#Override
public void onCompleted() {
t1.onCompleted();
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onError(Throwable e) {
t1.onError(e);
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onNext(T args) {
t1.onNext(args);
}
});
return Subscriptions.empty();
}
});
}
If you want to just use built in RxJava methods to order your observers, you can use flatMap and range to turn each item into multiple items each with a priority and then filter on priority. Observers are ordered based on how they filter.
Here's a trivial example:
Observable<Pair<Integer, Object>> shared = RxView.clicks(findViewById(R.id.textView))
.flatMap(c -> Observable.range(0, 2).map(i -> Pair.create(i, c)))
.share();
shared.filter(p -> p.first == 1)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "first subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "first subscribed onNext"));
shared.filter(p -> p.first == 0)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "second subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "second subscribed onNext"));
If you are doing this all over the place