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);
Related
I want to use coroutine in my kotlin app for must of it. but this app depends a lot on a service which return rxjava2 type object (so our input will be all the rxjava type). How can we still use coroutine in this model app instead of rxjava2. should we create a layer only convert rxjava object to normal object (or coroutine object?). or can we use both two together like:
SomeObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { value->
viewmodelScope.launch{
// send a flow or coroutine suspend fonction with value
}
},
Thanks,
You could wrap the RxJava functions with suspend functions.
See https://github.com/Kotlin/kotlinx.coroutines/issues/869
I want to answer my own question, It's not a good approche to use flow and rx at the same time. because it resolves the same problem. Rxjava + livedata is a clean architecture to go. or just use flow. so for resume:
Rxjava + livedata (a little bit coroutine to do some general
operation)
flow + coroutine
Two clean ways to make a good architecture. so if I have already rx type there is no need to convert to flow and reuse.
In Rxjava1 we can subscribe Subject to an Observable like this:
val asyncSubject = AsyncSubject<T>.create()
Observable<T>.subscribe(asyncSubject);
asyncSubject.subscribe(...)
Can't figure out how to achieve the same thing in Rxjava2 Flowable?
I'm thinking about something like this
Flowable<T>
.doOnComplete { t -> asyncSubject.OnComplete() }
.subscribe { t -> asyncSubject.onNext(t) }
Is there better/more concise way?
RxJava2 Separated things out into Flowable and Observable. Flowables are used when you want back-pressure.
Due to this break-out, we have two different types of Subject objects, your regular old BehaviorSubject, etc. which work with Observable, and a new set of classes called Processors, such as BehaviorProcessor, etc.
These classes work in mostly the same way as subjects, but are utilizable with Flowables instead of Observables.
In your case, you would want to use a Processor instead of a subject:
val proc = AsyncProcessor.create<Int>()
val flowable = Flowable.just(1)
flowable.subscribe(proc)
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.
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.
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