First time testing Rx, so please give some advice.
Need to test observable chain in model that gives as a result 21 data objects:
public Observable<BaseUnit> exec(int inputNumber) {
return getListObservable(inputNumber).subscribeOn(Schedulers.computation())
.flatMap(resultList -> getOperationsObservable()
.flatMap(operationElem -> getResultListObservable(resultList)
.flatMap(listElem -> Observable.just(listElem)
.flatMap(__ -> calculate(operationElem, listElem)
.subscribeOn(Schedulers.computation())))));
}
Here is some way I tried.
First try (here I have problems with get results and check them separately, because results of calculations inside each object unknown):
TestObserver<BaseUnit> observer = repository.exec(1000000)
.test()
.awaitDone(5, TimeUnit.SECONDS)
.assertNoErrors()
.assertValueCount(21)
.assertComplete();
Second way:
ArrayList<BaseUnit> result = new ArrayList<>();
Observable<BaseUnit> observable = repository.exec(1000000);
observable.subscribe(result::add);
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
assertNotNull(result);
assertEquals(21, result.size());
// here may be some loop of "assers"
Is that correct to test like I pointed it second way with time delay and loop?
How to check correctly that as a result it gives 21 object and check with condition that some property of data class is greater than zero? Is there any other cheks that need to be performed?
Also have one general question: need to test mvp presenter that use this model method and as a result display recieved values. How to do this using only JUnit4?
Related
I have a two queries which return two long values. I am setting these two long values to be displayed in individual text views. Finally I have a third text view which displays the combined value of both longs. I am having a problem getting the combined total to show as its setting the value before the livedata is returned.
Below is a snippet of the code
private void getData() {
mViewModelReframing.totalWorkouts(pkUserId).observe(getViewLifecycleOwner(), new Observer<List<ModelStatsTotalWorkouts>>() {
#Override
public void onChanged(List<ModelStatsTotalWorkouts> modelStatsTotalWorkouts) {
for (ModelStatsTotalWorkouts list : modelStatsTotalWorkouts) {
totalReframeWorkouts = list.getTotalWorkouts();
}
if (totalReframeWorkouts == 0) {
tvTotalReframes.setText(0 + getString(R.string.workouts_empty));
} else {
tvTotalReframes.setText("" + totalReframeWorkouts);
}
}
});
mViewModelCheckIn.totalWorkouts(pkUserId).observe(getViewLifecycleOwner(), new Observer<List<ModelStatsTotalWorkouts>>() {
#Override
public void onChanged(List<ModelStatsTotalWorkouts> tableCheckIns) {
for (ModelStatsTotalWorkouts list : tableCheckIns) {
totalCheckInWorkouts = list.getTotalWorkouts();
}
tvTotalCheckIns.setText("" + totalCheckInWorkouts);
// Combine both longs together for a combined total.
totalWorkouts = totalReframeWorkouts + totalCheckInWorkouts;
tvTotalWorkouts.setText("" + totalWorkouts);
}
});
}
Is there a better way to write the logic to achieve the desired result without the issue of the livedata not being returned fast enough?
Whenever you use independent Reactive streams like this (LiveData, RxJava, etc) you are going to have race conditions. You need to make explicit the dependencies for an action to happen - in this case your ability to update the UI in the way that you want had dependencies on BOTH APIs returning. This is the RxJava equivalent of zip. A few tips:
Consider using only a single Viewmodel for a view. The viewmodel should really be preparing data for your view specificially. In this case, it should really be that singular ViewModel that handles combining this data before passing it to your vew at all.
Barring that, since you've chosen LiveData here, you can do what you want by using a MediatorLiveData. Essentially, it acts as a composite stream source that depends on whichever other LiveData streams you add to it as described by that article. In this way, you can explicitly wait for all the needed values to arrive before you try to update the UI.
I solved the question by using this method:
public LiveData<List<ModelStatsTotalWorkouts>> totalWorkoutsCombined(long userId) {
LiveData liveData1 = database.getUsersDao().totalReframeWorkouts(userId);
LiveData liveData2 = database.getUsersDao().totalCheckInWorkouts(userId);
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
return liveDataMerger;
}
I have two slightly different Question classes. One is an retrofit call results object, and the other is a Room #Entity in my Android App.
And now I want from my Interactor class (Use-case) class do the following:
Make a call to the API and result (List where question is
the Retrofit response class)
On success, make a new Game object in my Room database. This operation have long (#Entity id which is autogenerated) as return
type.
for each Question from retrofit response (from (1)), question -> Converter which converts from retrofit.Question to
database.Question. Converter method takes 2 parameters, the
retrofit.Question object and the ID which was returned in step (2).
After conversion, add to database.
Observe on AndroidSchedulers.mainthread. (subscribeOn is called from repository)
Now the problem I am having is creating this stream with RxJava from my Interactor class.
Here is all the classes and calls. First is my Interactor.class method which should do the stream described above:
public Single<List<Question>> getQuestionByCategoryMultiple(String parameter);
The API CALL from MyAPI.class:
//this Question is of database.Question.
Single<List<Question>> getQuestionByCategory(String parameter);
The Room database repository.class:
Single<Long> addGameReturnId(Game game);
Completable addQuestions(List<Question> questions);
Converter.class:
public static List<database.Question> toDatabase(List<retrofit.Question> toConvert, int id);
I am having trouble creating the stream described above with these methods. I tried a mix of .flatmap, .zip, .doOnSuccess, etc without successfully creating the stream.
If there is anything else you need me to explain, or explain the problem better, please comment below.
public Single> getQuestionByCategoryMultiple(String parameters){
return openTDBType
.getQuestionByCategory(paramters) //step 1
// step 2
// step 3
.observeOn(AndroidSchedulers.mainThread()); //step 4
}
EDIT:
I tried something like this:
return openTDBType
.getQuestionByCategory(parameters)
.map(QuestionConverter::toDatabase)
.flatMap(questions -> {
int id = gameRepositoryType.addGameReturnId(new Game(parameters).blockingGet().intValue();
questions.forEach(question -> question.setqId(id));
gameRepositoryType.addQuestions(questions);
return gameRepositoryType.getAllQuestions(); })
.observeOn(AndroidSchedulers.mainThread());
^^ I don't know if this is the best way to go about this one? Can anyone confirm if this is a good way to design what I want to do here, or if there are better ways or any suggestions?
Try not use blockingGet especially when it is avoidable. Also, addQuestions won't be executed at all because it is not subscribed. You can add both addGameReturnId and addQuestions into the chain like this:
return openTDBType
.getQuestionByCategory(parameters)
.map(QuestionConverter::toDatabase)
.flatMap(questions -> {
return gameRepositoryType.addGameReturnId(new Game(parameters)) // returns Single<Long>
.map(id -> {
questions.forEach(question -> question.setqId(id));
return questions;
})
}) // returns Single<List<Question>> with the GameId attached
.flatMapCompletable(questions -> gameRepositoryType.addQuestions(questions)) // returns Completable
.andThen(gameRepositoryType.getAllQuestions()) // returns Single<>
.observeOn(AndroidSchedulers.mainThread());
Is there a simple way to mock observers in Objectbox? More specifically, I want my observer to be called when data is changed. Example:
private DataSubscription listen() {
return addressBoxStore.query()
.equal(Address_.address, address)
.build()
.subscribe()
.on(AndroidScheduler.mainThread())
.onError(this::handleError)
.observer(observeAddress());
}
private DataObserver<List<Address>> observeAddress() {
return addresses -> {
// should only be one address
if (!addresses.isEmpty()) {
// Run some code here
}
};
}
Given the above observer is registered, I want to add a junit test to make sure that my custom piece of code is called by Objectbox when data is changed. Is there a way to trigger events to occur in junit unit tests with some custom data so that I can verify some custom behaviour?
One approach is to mock the builder objects (example below), but that becomes ugly. Is there a better solution?
private void givenAddress(Answer answers) {
when(subscriptionBuilder.observer(any(DataObserver.class))).then(answers);
when(query.subscribe()).thenReturn(subscriptionBuilder);
when(queryBuilder.build()).thenReturn(query);
when(subscriptionBuilder.on(any())).thenReturn(subscriptionBuilder);
when(subscriptionBuilder.onError(any())).thenReturn(subscriptionBuilder);
when(queryBuilder.equal(any(Property.class), eq(ADDRESS))).thenReturn(queryBuilder);
when(addressBox.query()).thenReturn(queryBuilder);
underTest = new ClassToTest(addressBox);
}
This means that as soon as the observers are added, the data in the Answer is passed directly to it, but this solution is hard to read.
Code
Author author = baseRealm.where(Author.class).equalTo("id", mId).findFirst();
public boolean checkGlobalSyncStatus(Author author, List<Books> mBooks) {
final boolean[] isJobSynchronized = {false};
Observable.fromIterable(mBooks)
.filter(Books::isChanged)
.doOnNext(book -> isJobSynchronized[0] = true)
.just(author)
.flatMapIterable(Author::getAllBooks)
.filter(MyBook::isChanged)
.doOnNext(mBook -> isJobSynchronized[0] = true)
.just(author)
.flatMapIterable(Author::getAllWriters)
.filter(Writers::isChanged)
.doOnNext(jobPage -> isJobSynchronized[0] = true)
.subscribe();
return isJobSynchronized[0];
}
Problem
fromIterable(mBooks) is called from static-reference Observable BUT just(author) is called from instance-reference.
I only want to get this operation done in single query. I can make different observable for each and perform desired operation but that would be lengthy.
Why?
By doing so, SonarQube is giving me unsuccessful check and forcing me to remove instance-reference.
Any alternatives will be appreciated.
You are trying to use just() as an operator when it is really an observable. It looks like your intention is to use the passed in author to make a series of queries, and then check that any of the books associated with the author have "changed".
Additionally, you are trying to return a boolean value that likely has not been set by the time the return occurs. You may need to block and wait for the observer chain to finish if you want the value. More likely, you want the observer chain to finish if any book has changed.
Additionally, the series of steps where you set the flag to true come down to setting the flag to true the first time.
Instead of just(), use map() to rebind the original author into the observer chain. Use the toBlocking() operator to make the process synchronous.
Observable.fromIterable(mBooks)
.filter(Books::isChanged)
.toBlocking()
.subscribe( ignored -> isJobSynchronized[0] = true );
return isJobSynchronized[0];
Since the (presumably) asynchronous queries are no longer necessary to compute the value, remove RxJava:
return mBooks.stream()
.filter(Books::isChanged)
.anyMatch();
There is no reason to use RxJava here, however, the proper combination of operators would be as follows:
Author author = baseRealm.where(Author.class).equalTo("id", mId).findFirst();
public boolean checkGlobalSyncStatus(Author author, List<Books> mBooks) {
return Single.concat(
Observable.fromIterable(mBooks)
.any(Books::isChanged)
, // ---------------------------------------------
Observable.fromIterable(author.getAllBooks)
.any(MyBook::isChanged)
, // ---------------------------------------------
Observable.fromIterable(author.getAllWriters)
.any(Writers::isChanged)
)
.any(bool -> bool)
.blockingGet();
}
I'm new to RXJava and i'm having trouble understanding how to chain together the result of API calls.
I'm making two API calls using retrofit, A and B, which both return an observable List of objects. Both API calls are independent so I want to make both at the same time, but to achieve my final result, I need to first take the result of A, do some work, then combine that with the result of B to populate my list adapter.
Make API Call A
Make API Call B
Take A's result and create result X
Take Result of B + X and populate adapter
#GET("/{object_id}/object_a")
Observable<List<Object_A>> getObjectAList(
#Path("object_id") long object_id);
#GET("/{object_id}/object_b")
Observable<List<Object_B>> getObjectBList(
#Path("object_id") long object_id);
This is where I get lost trying to use RX java. I can take the result of api call A and do my work
but I'm not sure how to take the result I just generated and combine it with API Call B.
aService. getObjectAList(object_a.getID())
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(new Action1<List<Object_A>>() {
#Override
public void call(List<Section> sections) {
// Do Stuff Here...
// Now i need to take this result and combine it with API Call B...
}
});
I want to make both API calls at the same time, but i'm not sure how to chain together and combine API calls. Any help is appreciative.
Something like this?
Observable
// make api call for A list and B list
.combineLatest(getObjectAList(), getObjectBList(), new Func2<List<Object_A>, List<Object_B>, Object>() {
#Override
public Object call(List<Object_A> o, List<Object_B> o2) {
// Do what you need to do in the background thread
List x = createX(o);
List y = createY(x, o2);
return y;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object y) {
// UI thread, do what you need e.g. renders the list
mAdapter.setList(y);
}
});
Taking care of replacing the proper types should bring you quite close to the solution.
The question is : how would you combine results ?
Building a new result from List and List ? Combine A objects with B objects ?
Answer to this question help to find the right operator for your problem.
A simple example of combining results can be this :
getObjectAList().zipWith(getObjectBList(), (aList, bList) -> // combine both list to build a new result)).subscribe()
You can combine elements of the list too with another operator (combineLatest for example)
aObs = getObjectAList().flatMap(Observable::from);
bObs = getObjectBList().flatMap(Observable::from);
Observable.combineLatest(aObs, bObs, (a,b) -> // combine a object with b object).subscribe();
For all of this examples above, requests will be done in parallel by retrofit.
I'd probably do something like the following
Observable convertedObservable = getObjectAList
.map(object_as -> convertAToX(object_as));
Observable.combineLatest(convertedObservable, getObjectBList, (listx, listb) -> {
return listx.addAll(listb);
}).subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(r -> {
setAdapterWith(r);
});
Keep in mind this is using lambdas instead of anonymous classes but you should get the gist. Map is a great way of converting one object type to another (results of A to Results of X). So you can decide how convertAToX method works for you. Then you can use combineLastest on the converted A-X and B to return the list of R which updates your adapter
Ideally this is all in a ViewModel of some kind where getObjectAList and getObjectBList can me mocked on with Mock observables and you can test all the logic easily :)