Picture the situation in an MVP pattern where your presenter subscribes to a service returning an observer:
public void gatherData(){
service.doSomeMagic()
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new TheSubscriber());
}
Now the class TheSubscriber calls onNext a method from the view, say:
#Override public void onNext(ReturnValue value) {
view.displayWhatever(value);
}
Now, in my unit test I would like to verify that when the method gatherData() is called on a non-erroneous situation, the view's method displayWhatever(value) is called.
The question:
Is there a clean way to do this?
Background:
I'm using mockito to verify the interactions and a lot more of course
Dagger is injecting the entire presenter except for TheSubscriber
What have I tried:
Inject the subscriber and mock it in the tests. Looks a bit dirty to me, because if I want to change the way the presenter interacts with the service (Say not Rx) then I need to change a lot of tests and code.
Mock the entire service. This was not so bad, but requires me to mock a lot of methods and I didn't quite reach what I wanted.
Looked up around the internet, but no one seems to have a clean straight way of doing this
Thanks for the help
Assuming that you are using interfaces for service and view in a similar manner:
class Presenter{
Service service;
View view;
Presenter(Service service){
this.service = service;
}
void bindView(View view){
this.view = view;
}
void gatherData(){
service.doSomeMagic()
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(view::displayValue);
}
}
It is possible then to provide mock to control and verify behaviour:
#Test void assert_that_displayValue_is_called(){
Service service = mock(Service.class);
View view = mock(View.class);
when(service.doSomeMagic()).thenReturn(Observable.just("myvalue"));
Presenter presenter = new Presenter(service);
presenter.bindView(view);
presenter.gatherData();
verify(view).displayValue("myvalue");
}
I know its pretty late but may it helps someone, cause i searched pretty long for a solution to your question :D
For me it worked out to add a Observable.Transformer<T, T> as followed:
void gatherData() {
service.doSomeMagic()
.compose(getSchedulerTransformer())
.subscribe(view::displayValue);
}
private <T> Observable.Transformer<T, T> getSchedulerTransformer() {
if (mTransformer == null) {
mTransformer = (Observable.Transformer<T, T>) observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
return mTransformer;
}
void setSchedulerTransformer(Observable.Transformer<Observable<?>, Observable<?>> transformer) {
mTransformer = transformer;
}
And to set the Transformer just I just passed this:
setSchedulerTransformer(observable -> {
if (observable instanceof Observable) {
Observable observable1 = (Observable) observable;
return observable1.subscribeOn(Schedulers.immediate())
.observeOn(Schedulers.immediate());
}
return null;
});
So just add a #Before method in your test and call presenter.setSchedulerTransformer and it should be able to test this :)
hope this helps and is somehow understandable :D
Related
I'm not an expert on RxJava sorry if my question is a dummy question, I notice that my project is using RxLifeCycle dependency, I want to rid of it from the project due to the author advice, I wonder how I should remove it, it's just enough to create a composite disposable and dispose all my disposables at onDestroy() method? or is necessary to something else?
Thank you so much.
EDIT:
I provide some examples of the code
First, everything inherits from a base activity, this activity has this method which is used across all activities
public <T> Observable<T> bindRxToActivity(Observable<T> observable, boolean isSilent) {
if (!isSilent) {
loading(true);
hideKeyboard();
}
return observable
.compose(bindToLifecycle())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnEach(r -> handleDoOnEach())
.doOnNext(this::handleDoOnNext);
}
And it is used like this:
bindRxToActivity(intentApi.evaluation(), false)
.subscribe(
{ this.handleTransferEvaluationSuccess(it) },
{ this.evaluateTransferFailed(it) }
)
In all scenarios of the activities is used as above.
and for fragments:
public <T> Observable<T> bindRxToFragment(Observable<T> observable, boolean silent) {
if (!silent) {
this.loading(true);
listener.hideKeyboard();
}
return observable
.compose(bindToLifecycle())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnEach(r -> handleDoOnEach())
.doOnNext(this::handleDoOnNext);
}
And it is used like this in all fragments:
baseFragment.bindRxToFragment(baseFragment.r.loginApi().login(login), true)
.subscribe(
login1 -> loginSuccess(BaseActivity, login1, listener),
baseFragment::handleError
);
So what could be the best approach to use manual disposing of the observers?
Thanks
i'm working on project where i have to insert and delete data from room db , so basically i was using the old approach which is to implement Asynctask for background operations but since it is no longer recommended , i decided to use Rxjava instead , i tried to implement it but i'm not getting any result so far , and this is a piece of code where it shows the insertion of data
Completable.fromAction(new Action() {
#SuppressLint("CheckResult")
#Override
public void run() throws Exception {
recordingDb.insertRecording(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
And this is the deletion method
public void DeleteData(modelUidd modelUidd) {
Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
recordingDb.delete(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
So basically i tried to use completable with the operator fromaction , i'm not sure if what i implemented is correct or not , any help would appreciated guys , thank you
The problem is that you are actually not subscribing to the observables, so nothing is happening.
To subscribe to an observable, you have to call the .subscribe() method.
I suggest that your methods defined in your DAO classes (or you "repository" classes), such as DeleteData in your example, return the Observable. Then, you can call the method in the DAO to get the Observable and subscribe to it from (ideally) a ViewModel or, if not, directly from an Activity. The moment you call the subscribe you will trigger the actual insertion or deletion, and will get a response from the onSuccess or onError defined callbacks.
For example:
public class MyViewModel extends ViewModel {
private MyRepository myRepository;
private final CompositeDisposable disposables;
#Inject
public MyViewModel(MyRepository myRepository) {
...
this.myRepository = myRepository;
disposables = new CompositeDisposable();
...
}
public void callObservableInRepository() {
disposables.add(myRepository.myObservable()
.subscribe(onSuccess -> {...} , onError -> {...}));
}
#Override
protected void onCleared() {
disposables.clear();
}
}
You can also check these two other answers for more information:
About async operations in RxJava
Using CompositeDisposable in ViewModel
What is the proper way to execute some kind of "background" action, like saving data in the database, when this action is triggered by a presenter, but the presenter is disposing the observable before the end of the query?
Should I decouple my Repository observable from the UseCase one?
Example
I'me developing an Android application following the Clean Architecture and MVP patterns, using RxJava and Dagger.
In a Dialog, I'm listing devices, and can connect to one when selecting it in the list.
When an element in the list is clicked, the dialog is dismissed, and I'm setting/saving this device as the 'current' device in my repository layer and animating a logo in the toolbar of the activity (somehow like the Chromecast is doing).
In my presenter, I dispose my UseCases when the view is detached, so if the saving is not complete when the dialog is dismissed, the observable gets disposed and the device is not set as 'current' device.
Presenter
#Override
public void deviceClicked(String id) {
getMvpView().dismissView();
mConnectToDeviceUseCase.execute(id, new DisposableCompletableObserver() {
#Override
public void onComplete() {
Timber.d("Connected to device");
}
#Override
public void onError(Throwable e) {
Timber.e("Error while connecting to device: %s", e.getMessage());
}
});
}
UseCase
public class ConnectToDeviceUseCase extends UseCaseCompletableWithParameter<String, DevicesRepository> {
#Inject
public ConnectToDeviceUseCase(DevicesRepository DevicesRepository,
#Named("Thread") Scheduler threadScheduler,
#Named("PostExecution") Scheduler postExecutionScheduler) {
super(devicesRepository, threadScheduler, postExecutionScheduler);
}
#Override
protected Completable buildObservable(String id) {
Timber.d("GetDevicesUseCase buildObservable");
return repository.connectToDevice(id);
}
}
Repository
private ReplaySubject<DeviceConnection> connectionStatus = ReplaySubject.create();
...
#Override
public Completable connectToDevice(String id) {
Timber.d("connectToDevice IN");
return mDLNADataSource.getDevices()
.flatMapIterable(items -> items)
.filter(item -> item.id().equals(id))
.firstOrError()
.flatMapCompletable(this::saveAsCurrent)
.doOnSubscribe(a ->
{
Timber.d("connectToDevice doOnSubscribe");
connectionStatus.onNext(DeviceConnection.builder().setStatus(DeviceConnection.STATUS_CONNECTING).build());
}
)
.doOnComplete(() ->
{
Timber.d("connectToDevice doOnComplete");
connectionStatus.onNext(DeviceConnection.builder().setStatus(DeviceConnection.STATUS_CONNECTED).build());
}
)
.doOnError(a ->
{
Timber.d("connectToDevice doOnError");
connectionStatus.onNext(DeviceConnection.builder().setStatus(DeviceConnection.STATUS_NOT_CONNECTED).build());
}
)
.doOnDispose(() ->
{
Timber.d("connectToDevice doOnDispose");
});
}
I am neither java nor Android expert but what you have described in your question sounds like controlflow issue to me.
If you trigger some async activity (e.g. save) you should also connect the cleanup to it. One possible solution could be using the concept of a "future" or "promise". Another one could be the actor pattern where save and cleanup would be messages executed sequentially.
In an effort to implement 'clean' architecture on android with the mvp pattern it is advised to treat the android framework as a plugin and not leak any android aware dependencies into the presenter layer. Using rxjava, if I have a presenter that is designed to 'push' data to the view layer I may want to have logic like this:
public interface SearchPresenter {
interface ViewLayer {
void updateResults(List<SearchResult> searchResults)
}
void bind(ViewLayer viewLayer);
void unbind();
}
public class SearchPresenterImpl implements SearchPresenter {
ViewLayer viewLayer;
CompositeDisposable compositeDisposable;
#Override
public void bind(ViewLayer viewLayer) {
this.viewLayer = viewLayer;
compositeDisposable = new CompositeDisposable();
compositeDisposable.add(
searchInteractor.getSearchResults()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::refreshView));
}
private void refreshView(List<SearchResult> searchResults) {
//send the results back to the view layer to update a RecyclerView
viewLayer.updateResults(searchResults)
}
#Override
public void unbind() {
compositeDisposable.dispose();
}
However, by observing on 'AndroidSchedulers.mainThread()' this forces a dependency on:
import io.reactivex.android.schedulers.AndroidSchedulers
at which point, my presenter now knows about android and is tied to it; which I want to avoid.
What is the advised way to handle this so that the result is guaranteed to be delivered to the ViewLayer on android's UI (Main) thread while the presenter maintains no dependencies on anything android related?
AndroidSchedulers.mainThread() uses Android code to schedule actions on the main thread, don't use it directly in your presenter.
instead You can refactor the presenter to take Schedulers in its constructor, in production code use the regular AndroidSchedulers.mainThread() and Schedulers.io(), in testing you can just send Schedulers.trampoline() or Schedulers.immediate() .
see this pattern in this example: https://github.com/riggaroo/GithubUsersSearchApp/blob/master/app/src/main/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenter.java
and its test class here:
https://github.com/riggaroo/GithubUsersSearchApp/blob/8b83095d7a2cc8f3cb69a945224ab4c37cf54a37/app/src/test/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenterTest.java
As pointed in previous answer you can inject Scheduler in constructor of Presenter but there is at least two more possibilities.
You can inject Scheduler directly to interactor and then you don't need to do any manipulation in presenter. Then in tests you can mock your interactor and completely forget about rxjava dependency in your tests.
You can use RxJavaPlugins and RxAndroidPlugins to override schedulers in tests if you feel that it is fine for you to keep AndroidSchedulers in interactors.
So define (Kotlin):
object RxJavaPluginHelper {
fun setup(scheduler: Scheduler = Schedulers.trampoline()) {
RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> scheduler }
RxJavaPlugins.setComputationSchedulerHandler { scheduler }
RxJavaPlugins.setIoSchedulerHandler { scheduler }
RxJavaPlugins.setNewThreadSchedulerHandler { scheduler }
RxJavaPlugins.setSingleSchedulerHandler { scheduler }
}
fun teardown() {
RxAndroidPlugins.reset()
RxJavaPlugins.reset()
}
}
And then use
companion object {
#BeforeClass
#JvmStatic
fun setup() {
RxJavaPluginHelper.setup()
}
#AfterClass
#JvmStatic
fun teardown() {
RxJavaPluginHelper.teardown()
}
}
Personally I would not fight for removing this line from presenter, as whole idea behind removing Android imports from Presenter is to make it portable to different platforms, but since it is unlikely to happen I would treat it as ok.
Recently I saw an RxJavaarticle explaining Transformers and highlighting how they could be used to reuse Schedulers. I tried to use this and inside the same class, this method works fine:
<T>Observable.Transformer<T, T> applySchedulers() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
I want to move this into a helper class with a static method so I can use it in my whole Androidapp. But when I try to use the method of this class
public class RxFunctions {
public static <T>Observable.Transformer<T, T> applySchedulers() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
inside another class
public void requestLoginState() {
restClient.requestLoginState()
.compose(RxFunctions.applySchedulers())
.subscribe(new TimberErrorSubscriber<LoginStateResponse>() {
#Override
public void onCompleted() {
}
...
it will no longer recognise my Subscriber, error: Cannot resolve method 'subscribe(anonymous com.example.util.rx.TimberErrorSubscriber<com.example.network.retrofit.response.login.LoginStateResponse>)'
I'm using Java8without Retrolambda.
Changing the compose line to
.compose(this.<LoginStateResponse> RxFunctions.applySchedulers())
results in an error for the LoginState type saying Reference parameters are not allowed here
I'm fairly new to RxJava and grateful for any advice.
Edit: now Android does support java 8
You say you are using Java8 on android, but android does not support Java8 without plugins like retrolambda, so I'll asume that you are actually compiling with Java6 (since Java7 only works for KitKat and above).
In that scenario, you might need to make explicit the parametric type of your applySchedulers. I believe you tried to do that when you wrote this.<LoginStateResponse>, but that would only work if your generic method is inside your current class.
In your case what you actually need is to specify the type on the static method call:
.compose(RxFunctions.<LoginStateResponse>applySchedulers())