How to test Retrofit2 + RxJava2 api call? - android

I am wondering how to test Retrofit2 call via rxjava2. My retrofit api interface looks like:
public interface LoginApiMapping {
#POST("v1/secm/oam/oauth2/token")
Observable<Response<RestResponseHolder<LoginResponseModel>>> login(#Body LoginModel model);
}
and i would like to write the test which will send this request via RxJava2 and check the response. I consider that there is the problem with RxJava as it is asynchronous and the test finishes before i get the response, so i tried to use TestSubscriber as below, but it's not possible to subscribe TestSubscriber<Response<RestResponseHolder<LoginResponseModel>>> as i expected
#Test
public void loginTest(){
Context appContext = InstrumentationRegistry.getTargetContext();
Api t = TestApi.create(appContext);
LoginModel loginModel = new LoginModel("username","password");
TestSubscriber<Response<RestResponseHolder<LoginResponseModel>>> testSubscriber = new TestSubscriber<>();
t.get(LoginApiMapping.class).login(loginModel).subscribe(testSubscriber);
}
anyone who solved that? thanks

I finally found the solution, so i decided to post it here.
First you have to call toFlowable() on the observable and set the Backpressure strategy and then you can subscribe with TestSubscriber as below
t.get(LoginApiMapping.class).login(loginModel).toFlowable(BackpressureStrategy.DROP).subscribe(testSubscriber);
Then add
testSubscriber.awaitTerminalEvent();
at the end that you can check various assertions on testSubscriber as R. Zagórski pointed in answer above.

At the end of of test add:
testSubscriber.awaitTerminalEvent();
Then you can check various assertions on testSubscriber.

You use TestSubscriber with Flowable and TestObserver with Observable. You can test Single, Completable and Maybe by converting them to either Flowable or Observable, but the built-in test()s for these three return TestSubscriber.

Related

Nested Network calls using Rx-Android and Retrofit

I am working on an application, where I had used Rx-android and Retrofit to do the network request, but now there is a requirement in the project where I have to do the nested network calls.I tried to google it out but didn't found any good article.If any one has worked on such topic then please let me know your findings.
Assuming you're using retrofit with the rxjava adapter:
fun firstRequest(): Single<Response<String>>
fun secondRequest(idFromFirstRequest: String): Single<Response<ResponseBody>>
Use flatmap operator to chain the network calls:
firstRequest()
// do more operators on the request, like transforming the object, or showing it first on the ui
.flatMap { stringId -> secondRequest(stringId) }
// you can flatMap here again to chain another network requests
// .flatMap { thirdRequest() }
// .flatMap { fourthRequest() }
// and so on...
There are various articles related to API chaining, and the easiest way to achieve is using Rx-Java approaches
1) Using RxJava Zip operator (for parallel requests)
2) Using RxJava flatMap() operator(To request serially one after another)
Refer these two links for more detail examples
Synchronizing Network Calls With RxJava
Multiple api request using retrofit and rx java

Adavantage of Retrofit with RxJava externally

AFAAIK, Retrofit uses RxJava internally.Then what is the advantage of integrating Retrofit with RxJava externally like here, if I don't want to filter,sort or modify the data received from api?Does it reduces the time for fetching response from api?In what way does it helps in improving performance of our api calls?
Retrofit started as project before RxJava and you used to retrieve the API via callbacks. Then came RXJava and a more strict integration between the two was possible. So that you change Call<T> with an Observable/Flowable interface, and instead to use a call back into the code you retrieve the result directly exploiting the power of the reactive paradigm.
Please consider you have to specify you are using RXJava when you build Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl);
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//here
.addConverterFactory(GsonConverterFactory.create())
.build();
Saying that RXJava implements internally Retrofit is kind of tricky, Retrofit remain indipendent, just RXJava offers some binding code so that you can adapt Retrofit2 to be an Observable.
This code taken from here, explain how to bind the two
public interface UserService {
#POST("/me")
Observable<User> me();
}
// this code is part of your activity/fragment
Observable<User> observable = userService.me();
observable
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
// handle completed
}
#Override
public void onError(Throwable e) {
// handle error
}
#Override
public void onNext(User user) {
// handle response
}
});
Then you ask to many questions, in stackoverflow you get one reply per post.
Just please consider that the advantage of using RXJava and Retrofit integrated are a lot, for instance you have a come much more clean, testable and you do not have to consider concurrency issues. Regarding the performance I get is the same for a normal case.
EDIT:
To understand better when to use RXJAVA+Retrofit and when just Retrofit you can see this post
Outside from that content please consider that is really useful to see all the succesion in a functional way inside a single class, plus you have OnComplete, you can operate any sort of transformation.
Furthermore is much easier to combine multiple calls as here, the advantages are really clear in real life situations,
and also to do testing and maintain the code clean, that just taken alone these two, are two great advantages.
You also may want to explore the new Google Architecture functionalities components with Retrofit, where you can use both RXJava or LiveData

Can't subscribe on a RxJava 2 Observable with TestSubscriber

Why is my compiler not allowing myself to subscribe on an Observable with a TestSubscriber?
Here's my code:
TestSubscriber<User> testSubscriber = new TestSubscriber<>();
Observable.just(new User()).subscribe(testSubscriber);
And it's saying that it can't resolve method subscribe which is taking this parameter. But in all RxJava testing tutorials, they are using TestSubscriber without such problems. What can I do to test such Observable?
It is because *Subscriber are meant for Flowable while Observable uses the *Observer classes. This is because the reactive-streams standard reserves Subscriber for the fully compliant Publisher interface which Flowable implements.
Additionally with RxJava2 all reactive classes have a .test() method which will directly give you the appropriate testing object.
For RxJava 1 → RxJava 2 migration, I've just replaced TestSubscriber with TestObserver to make Observable#subscribe call compile and work.
You can change Observable to Flowable if you want to test with TestSubscriber as follow:
TestSubscriber<User> testSubscriber = new TestSubscriber<>();
Flowable.just(new User()).subscribe(testSubscriber);

Subscribe to PublishSubject in RxJava 2.0

I have the following code which worked perfect in RxJava 1.6.
private PublishSubject<Boolean> mConnectionSubject;
...
Observable.create(subscriber -> {
mConnectionSubject.subscribe(subscriber);
...
});
But after migration to RxJava 2.0 this code doesn't compile. The reason is that Observable.create() now accepts as argument ObservableEmitter. But PublishSubject doesn't accept ObservableEmitter. It accepts only Consumer or Observer interface.
Will appreciate any suggestions.
I would do something like below in the scenario you have mentioned.
Observable.<Boolean>create(emitter -> mConnectionSubject.subscribe(
emitter::onNext,
emitter::onError,
emitter::onComplete
));
You don't need to create a new observable, this should work:
private PublishSubject<Boolean> mConnectionSubject;
...
mConnectionSubject.doOnNext(...).observeOn(...).subscribe();
You can chain multiple operators/subscriptions to the PublishSubject.

RXJava Android - Observable result needed to create another observable

I cant find a way to combine or chain a list of observables that it´s responses are prerequisites to other call that creates another Observable.
I´m using retrofit with observables.
My Service:
String url = "/geocode/json?sensor=false";
#GET(url)
Observable<GeocodeResult> getReverse(#Query("key") String gMapsKey,
#Query("latlng") LatLng origin);
And another service needs that GeocodeResult
#POST("/api/orders")
Observable<Order> createOrder(#Body GeocodeResult newOrder);
And I´m trying with:
// Prerequisite 1
Observable geocodeObservable = Address.get(...);
// Call createOrder after geocode is obtained?
return Observable.combineLatest(geocodeObservable, geocode -> createOrder(geocode));
But it don´t work because combineLatest needs an object, not an observable but I need to return the observable.
With JoinObservable:
Pattern5<Geocode> pattern = JoinObservable.from(geocodeObservable)
Plan0<Observable<Order>> plan = pattern.then(Order::create);
return JoinObservable.when(plan).toObservable().toBlocking().single();
But it throws an NoSuchElementException exception. Why?
I do toBlocking().single() because I need the Observable and not the Observable<Observable<Order>> :(.
Or how can I do it?
You could try using flatMap which can take the second observable as an parameter.
The function takes the items emitted by the first observable and creates an observable for each of those items and then flattens the items emitted by those observables into a single observable. This sounds complex, but fortunately both your Retrofit functions emit only a single item, so only one observable gets "flattened" into a observable.
You can use flatMap like this:
restApi.getReverse(gMapsKey, origin)
.flatMap(geocodeResult -> createOrder(geocodeResult))
.subscribe(order -> doSomething(order));
combineLatest doesn't really fit your needs, because it would perform both REST calls at the same time, not one after the other, so you can't use the response of the first one as the parameter of the second. I can't comment on why the exception gets thrown for JoinObservable because it's not a part of any public API. Also toBlocking() shouldn't really be used for anything other than testing.
I ended up creating a new Object and using Observable.combineLatest to combine all the prerequisites creating a new Observable and then using flatMap to create the new Observable from that observable.
Observable<NewOrderWrapper> newOrderObservable = Observable.combineLatest(prerequisites, (param1, param2,...) -> {return new NewOrderWrapper(param1, param2,...)});
and then
Observable<Order> finalOrderObservable = newOrderObservable.flatMap(newOrderWrapper -> create(newOrderWrapper))
Check a post here MakinGIANST/RXJava post.
Thanks to #LukaCiko

Categories

Resources