How verify the order of mock methods in JUnit - android

I have this class with these structure and i need test the behaviour of OnRequestListOfLunchsFinished interface
#Override
public void getListOfLunchs(final OnRequestListOfLunchsFinished callback) {
zip().onErrorResumeNext(new Function<Throwable, ObservableSource<? extends LunchServiceResponse>>() {
#Override
public ObservableSource<? extends LunchServiceResponse> apply(#NonNull Throwable throwable) throws Exception {
callback.onError(new RuntimeException(throwable));
callback.onEnd();
return Observable.empty();
}
}).subscribe(new Consumer<LunchServiceResponse>() {
#Override
public void accept(LunchServiceResponse response) throws Exception {
List<Lunch> result = new ArrayList<>();
List<IngredientResponseVO> ingredients = response.getIngredients();
Map<Integer, Ingredient> hash = new HashMap<Integer, Ingredient>();
for (IngredientResponseVO vo : ingredients)
hash.put(vo.id, new Ingredient(vo.id, vo.name, new BigDecimal(vo.price.toString()), vo.image));
for(InfoLunchResponseVO vo: response.getLunch()){
Lunch lunch = new Lunch();
lunch.setId(vo.id);
lunch.setImage(vo.image);
lunch.setName(vo.name);
for(Integer id : vo.ingredients){
Ingredient ingredient = hash.get(id);
lunch.addIngredient(ingredient);
}
result.add(lunch);
}
callback.onSuccess(result);
callback.onEnd();
}
});
callback.onStart();
}
private Observable<LunchServiceResponse> zip(){
return Observable.zip(getRequestOfListOfLunchs(), getRequestOfListOfIngredients(), new BiFunction<List<InfoLunchResponseVO>, List<IngredientResponseVO>, LunchServiceResponse>() {
#Override
public LunchServiceResponse apply(#NonNull List<InfoLunchResponseVO> infoLunchResponseVOs, #NonNull List<IngredientResponseVO> ingredientResponseVOs) throws Exception {
return new LunchServiceResponse(infoLunchResponseVOs, ingredientResponseVOs);
}
});
}
i have this test method
#Test
public void teste(){
List<IngredientResponseVO> ingredients = Collections.emptyList();
List<InfoLunchResponseVO> lunchs = Collections.emptyList();
when(mockApi.getListOfIngredients()).thenReturn(Observable.just(ingredients));
when(mockApi.getLunchs()).thenReturn(Observable.just(lunchs));
mockImplementation.getListOfLunchs(callback);
InOrder order = inOrder(callback);
order.verify(callback).onStart();
order.verify(callback).onSuccess(anyList());
order.verify(callback).onEnd();
order.verifyNoMoreInteractions();
}
but i am receiving the exception:
org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure
Wanted but not invoked:
callback.onSuccess(<any>);
if i do this:
callback.onStart();
callback.onSuccess(Collections.<Lunch>emptyList());
callback.onEnd();
InOrder order = inOrder(callback);
order.verify(callback).onStart();
order.verify(callback).onSuccess(anyList());
order.verify(callback).onEnd();
order.verifyNoMoreInteractions();
this works.
how verify only calls of my mock callback?

You just must not use the InOrder object.
mockImplementation.getListOfLunchs(callback);
Mockito.verify(callback).onStart();
Mockito.verify(callback).onSuccess(anyList());
Mockito.verify(callback).onEnd();
Mockito.verifyNoMoreInteractions();

AFAICS the issue is not with the test but with your reading of the test results (jumping ahead: I believe it found a bug in your code).
Probably in the real code your getListOfIngredients and getLunchs do some network requests i.e. they are asynchronous to the call to getListOfLunchs and (zip inside of it). Thus in the real code onStart is called immediately on the caller thread while onSucess and onEnd are called later. However in your test you mock those API calls with very synchronous Observable.just and thus the order of execution is different: first onSuccess is called, then onEnd and finally onStart (you can easily validate this if you substitute your mocked callback with a custom one that just logs method name in every call).
You probably expeceted that since you use verifyNoMoreInteractions you would get a error about wrong order of onStart. Unfortunatelly this is not how it works. Since your order verifications are specified earlier, they are checked earlier. And in those checks there is yet no restriction of "no more". So what happens is roughly following:
onSucess is called. InOrder check ignores it because there was no onStart yet
onEnd is called. InOrder check ignores it because there was no onStart yet
onStart is called. This matches what InOrder expects and now it waits for onSucess. However this (second) onSuccess never comes and this is exactly what the error says.
So what to do? First of all I'd like to say that IMHO this failed test did find a very real bug in your code. Assume that at some point in the future someone added a caching layer to your API so sometimes getListOfIngredients and getLunchs return immediately with a synchronous result. In such case your code breaks contract of the OnRequestListOfLunchsFinished that onStart should be called first. So the proper way is to fix your code. An obvious but possible wrong way is to move the line
callback.onStart();
to the start of the method. (Why it is possibly wrong? Can your zip throw an Exception? If it does, what happens to the state of the callback?). Another way is to do the same as you do with onEnd i.e. copy it inside both success and error handling code in proper order.

Related

Troube with getting a Single<T> call to work in RxJava and Android

I have a MVVM architecture in my Android app. In an activity, I invoke a method to try to create something from service/repository and return it. I am using RxJava.
Here is the flow:
I click something in view, it invokes method in the Activity.
Method in Activity invokes method in ViewModel.
Method in ViewModel invokes method in Interactor(/use-case).
Interactor has access to service and tries to create something from that service.
Here is the code for this:
Activity:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password);
}
ViewModel:
public class AddWalletViewModel extends BaseViewModel {
private AddWalletInteractor addWalletInteractor;
private final MutableLiveData<Wallet> newWallet = new MutableLiveData<Wallet>();
private final MutableLiveData<ErrorCarrier> newWalletError = new MutableLiveData<ErrorCarrier>();
public LiveData<Wallet> newWallet() {
return newWallet;
}
public AddWalletViewModel(AddWalletInteractor addWalletInteractor) {
this.addWalletInteractor = addWalletInteractor;
}
public Single<Wallet> createWallet(String password){
return addWalletInteractor.addWallet(password)
.subscribe(wallet -> newWallet.postValue(wallet), this::addErrorToLiveData);
}
private void addErrorToLiveData(Throwable throwable){
newWalletError.postValue(new ErrorCarrier());
}
}
Interactor:
public class AddWalletInteractor {
private final KeyStoreServiceInterface keyStoreServiceInterface;
public AddWalletInteractor(KeyStoreServiceInterface keyStoreServiceInterface) {
this.keyStoreServiceInterface = keyStoreServiceInterface;
}
public Single<Wallet> addWallet(String password){
return keyStoreServiceInterface.
createWalletAndReturnWallet(password);
}
}
Service:
#Override
public Single<Wallet[]> getAllWallets() {
return Single.fromCallable(()-> {
Accounts accounts = keyStore.getAccounts();
int amount = (int) accounts.size();
Wallet[] wallets = new Wallet[amount];
for (int i = 0; i<amount; i++){
org.ethereum.geth.Account gethAccount = accounts.get(i);
wallets[i] = new Wallet(gethAccount.getAddress().getHex().toLowerCase());
}
return wallets;
}).subscribeOn(Schedulers.io());
}
Problem is I can not manage to get this to work by tweaking the code. Right now it forces me to cast to (Single) in the return of the createWallet() method in the viewmodel. When running the app, it crashes in that method with:
java.lang.ClassCastException:
io.reactivex.internal.observers.ConsumerSingleObserver cannot be cast
to io.reactivex.Single
at addwallet.AddWalletViewModel.createWallet(AddWalletViewModel.java:31)
Please keep in mind I am new to RxJava, I am still trying to figure it out. Any suggestions here?
The cast performed in the createWallet method will always fail.
Solution 1
The simplest way to fix the crash is to change the return type of that method to io.reactivex.disposables.Disposable, assuming you're using RxJava 2. If you're using RxJava 1, then have it return rx.Subscription. The code you presented that calls the createWallet method doesn't seem to use the returned value so it shouldn't make a difference.
Solution 2
If you really do need the return type to be Single and you want to keep the same behavior, then an alternate solution would be to change the createWallet method as follows:
public Single<Wallet> createWallet(String password) {
return addWalletInteractor.addWallet(password)
.doOnSuccess(wallet -> newWallet.postValue(wallet))
.doOnError(this::addErrorToLiveData);
}
The method now returns a new Single that does whatever the Single returned from addWallet does and additionally invokes the appropriate lambda function when a value is successfully emitted or an error occurs. You would also need to modify the call site for the method as follows:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password).subscribe();
}
That subscribe call is needed to have the Single start emitting values. It takes no parameters because you already do all of the interesting work in the createWallet method itself. Both snippets were written with RxJava 2 in mind, but I believe they will also work in RxJava 1 as is.
If you haven't already done so, you should check out the official Rx website as it provides a ton of information on how reactive streams work and how to use them.
Since you're new to RxJava and the documentation is so vast, here's a brief overview of the subscription concept and how it applies to your situation.
RxJava and other stream-based libraries like it have two main components: producers and consumers. Producers supply values and consumers do something with those supplied values.
Single is a kind of producer that only produces one value before terminating. In your case, it produces a reference to the newly created wallet. In order to do something with that reference, it needs to be consumed. That's what the subscribe method on the Single class does. When the Single returned by the addWallet method produces a value, the lambda passed to the subscribe method is invoked and the wallet parameter in that lambda is set to the produced value.
The return type of the subscribe method is NOT itself a Single. When a consumer and a producer are coupled together by the subscribe method, it forms a connection which is represented by the Disposable class. An instance of that class has methods to cancel the connection before the producer is done producing values or to check if the connection has been cancelled. It is this connection object that is returned by the subscribe method.
Note that until this connection is made via one of the subscribe overloads, the producer will not start producing items. I.e., a Single that is never subscribed to will never do anything. It's analogous to a Runnable whose run method is never called.

Unit Testing: How to verify and mock onCompleted for an Observable in RxJava within Android

I am trying to write some unit tests for an Android application that is using Retrofit 2, Mockito 1.10 and RXJava 1.0. I am not using a java version that supports lambdas!
My code uses Observables and I can do the following:
when(myAPI.Complete(anyString(), any(MyContainer.class)))
.thenReturn(Observable.<GenericResponse>error(new Throwable("An error has occurred!")));
Subscriber genericResponseSubscriber = mock(Subscriber.class);
myPresenter.myUseCase(id, container, genericResponseSubscriber);
verify(genericResponseSubscriber, times(1)).onError(any(Throwable.class));
The above code works fine and allows me to throw an error and capture it within the test.
What I need to be able to do as well (of course) :) is to capture positive conditions. I feel like it's obvious but can't find the answer I need.
How can I capture onComplete and onNext cases ?
I know that the verification for onComplete would be...
verify(genericResponseSubscriber, times(1)).onCompleted();
But I can't see what my 'when' clause should be. I tried the following but that fails:
GenericResponse response = new GenericResponse();
response.setSuccess(true);
when(myAPI.orderComplete(anyString(), any(MyContainer.class)))
.thenReturn(Observable.just(response));
Subscriber genericResponseSubscriber = mock(Subscriber.class);
myPresenter.myUseCase(id, container, genericResponseSubscriber);
verify(genericResponseSubscriber, times(1)).onCompleted();
The failure here is that subscriber.onStart() was instead called.
So, what I would like to know is, how I can mock and verify the 'onComplete' and 'onNext' calls, please and more importantly what I should have looked to be able to have resolved this myself rather than having to ask! :)
As always, any help is appreciated.
Edit..
My onError working test case..
public void UseCaseOnError() throws Exception {
String id = "5";
 
order Order = new Order();
SomeContainer myContainer = new SomeContainer(order);
 
when(myRetroFitAPI.complete(anyString(), any(SomeContainer.class)))
.thenReturn(Observable.error(new Throwable(“My error!")));
 
Subscriber genericResponseSubscriber = mock(Subscriber.class);
 
orderPresenter.doUseCase(id, myContainer, genericResponseSubscriber);
 
verify(genericResponseSubscriber,times(1)).onError(any(Throwable.class));
 
}
What I should really add is that, I feel there should be an equivalent for onError in terms of a positive state, i.e. onCompleted. If I do the same but with onCompleted instead, my verification fails as it detects onStart has been called instead which I am finding rather confusing.
I have tried using the ReplaySubject as such:
public void createOrderOnCompleteError() {
orderOnCompleteSubject.onError(new Throwable("I am an error"));
}
public void createOrderOnCompleteSuccess() {
orderOnCompleteSubject.onNext(new GenericResponse().setSuccess(true));
orderOnCompleteSubject.onCompleted();
}
The error mechanism works fine.. the completed mechanism does not...
You should use the class TestObserver for testing the Observable, in this way:
public Observable<Integer> getObservable() {
return Observable.just(12, 20, 330);
}
#Test
public void testObservable() {
Observable<Integer> obs = getObservable();
TestObserver<Integer> testObserver = TestObserver.create();
obs.subscribe(testObserver);
testObserver.assertComplete();
testObserver.assertResult(12, 20, 330);
}
In this way you can verify that it completes and emits all the expected items.
If you want to create a mocked version of your observable, you can just create a new Observable that has the behaviour that you want. For example:
public Observable<Integer> mockedObservableCompleteWithResult() {
return Observable.create(e -> {
e.onNext(12);
e.onNext(20);
e.onNext(330);
e.onComplete();
});
}
that can be verified with the above-mentioned test.
Then we can create other mock for modelling other results
public Observable<Integer> mockedObservableError() {
return Observable.create(e -> {
e.onNext(12);
e.onError(new Throwable("Generic exception"));
});
}
That can be verified:
#Test
public void testObservable() throws Exception {
Observable<Integer> obs = mockedObservableError();
TestObserver<Integer> testObserver = TestObserver.create();
obs.subscribe(testObserver);
testObserver.assertError(Throwable.class);
}
Instead of mocking the Subscriber, you should create a TestSubscriber for RxJava 1:
when(myAPI.Complete(anyString(), any(MyContainer.class)))
.thenReturn(Observable.<GenericResponse>error(new Throwable("An error has occurred!")));
TestSubscriber genericResponseSubscriber = TestSubscriber.create();
myPresenter.myUseCase(id, container, genericResponseSubscriber);
// To check for an error
genericResponseSubscriber.assertError(Throwable.class)
// To check for completion
genericResponseSubscriber.assertCompleted()
You might need to be a bit more specific about which error class you expect. Check out the TestSubscriber documention. There is tons of more stuff you can verify with this class.
Happy testing!
The easy way is to try throwing a mock exception than the real one.
#Mock
Exception mockException;
observer.onError(mockException);

Android test method called after presenter executes retrofit call

I'm using Mockito to test my views but my tests are failing because of a method that is supposed to be called after a retrofit call is complete. How can I mock a view who's method is called by presenter after completion of a retrofit call? I'd like to verify that unBlockUI() below has been called. My tests show blockUI() is called but unblockUI() is not being called.
I get a fail message
Wanted but not invoked:
view.unBlockUI();
In my presenter I have the method
public void fetchResults(){
view.blockUI();
ResultsDataService resultsDataService = new ResultsDataService()
resultsDataService.getAllResults(new Callback<Catalog>() {
#Override
public void onResponse(Call<Catalog> call, Response<Catalog> response) {
view.unBlockUI();
}
#Override
public void onFailure(Call<Catalog> call, Throwable t) {
view.unBlockUI();
t.printStackTrace();
}
})
}
Results data service.
public class ResultsDataService {
private final RestApi restApi;
public CatalogDataService() {
//here I have a class that builds the REST Service
restApi = RestServiceBuilder.createService(RestApi.class);
}
public void getAllResults() {
Call<Catalog> call = restApi.getAllResults();
call.enqueue(callback);
}
}
my test method
#Test
public void shouldFetchAllResults_allOK() {
presenter.fetchResults();`
verify(view).blockUI();//This is called
verify(view).unBlockUI();//this is not called
}
I think one possible solution is to mock ResultsDataService to call the onResponse method of any callback every time getAllResults is called.
Unfortunately, the way you're creating your ResultsDataService inside fetchResults makes it really hard to do this. This is what we call tight coupling. You have a method that depends strictly on ResultsDataService with no chance to change it. Therefore you cannot control the presenter from the outside. As a rule of thumb, every time you see the new operator that's a sign of tight coupling.
Usually we use dependency injection to solve this. One way you can do it in your code is simply change the fetchResults method to receive the service as an argument:
public void fetchResults(#NonNull ResultsDataService service) {
// ...
}
It might not seem much, but now in the test you can pass in a configured mock and in your app you just pass in the real service.
Say now in your test you'd configure a mock like so:
ResultDataService service = mock(ResultDataService.class);
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Call call = (Call) invocation.getArgument(0);
call.onResponse(call, <some response here>);
return null;
}
}).when(service.getAllResults(any(Call.class)));
You can now use this to pass it to your presenter fetchResults.
What does the mock above do? It will call the onResponse method of the passed in argument. So basically it will call right away the onResponse callback when you call fetchResults. In your case this will in turn call unBlockUI.
Notice you can do something similar to test the onFailure. You should also make ResultsDataService an interface, so your presenter doesn't depend on concrete implementations, but just interfaces. This is much more flexible.
Hope this helps. Remember, this is one way of doing this and not the single way.

Test void method using mokito

How do I test below method using mockito
public void showArg(String ss) {
if(ss == null) {
throw new NullPointerException();
}else if(ss.equals("")) {
throw new IllegalArgumentException();
}
// Log.d("",""+ss);
if(ss.equals("xyz")) {
this.show();
}else {
getResult(0);
}
}
In this example, there is nothing to be mocked. I just want to test the that is appropriate methods are called based on i/p.
If you want to verify that this method was called (assuming it was public), I suggest using a spy...
MyClass spy = Mockito.spy( myActualObject );
spy.showArg("xyz");
Mockito.verify(spy).show();
Spying (instead of mocking) means to take an actual object and "spy" on it, by wrapping it in another instance. This way you can call actual methods, but also check what was called and even modify what some methods will do, similar to mocking (the difference is, that a mock does not have an underlying "real" object, while a spy has).
As already mentioned you should use a spy to test such code. Additionaly looking at your code you should also test whether appropiate exceptions are thrown.
Code testing border cases can be looking like this:
#Test(expected = NullPointerException.class)
public void shouldThrowNullPointerExceptionWhenNullStringProvided() {
showArg(null);
}
#Test(expected = IllegalArgumentException.class)
public void shouldThrowIllegarArgumentExceptionWhenEmptyStringProvided() {
showArg("");
}

Understanding how to test RxJava Observables

I am learning how to write android unit tests. And, I am looking at examples: So, I saw something like this:
#Test
public void getPopularMoviesMakesApiCall() {
// given that the api service returns a response
1. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));
// when getPopularMovies is invoked
2. mRemoteRepository.getPopularMovies(1).subscribeWith(mMovieListTestSubscriber);
// then, verify that the api request is made and returns the expected response
3. verify(apiService).discover(SORT_BY_POPULARITY, PAGE, API_KEY);
4. mMovieListTestSubscriber.assertValue(mMovieList);
}
I tried to run it, and I noticed option 1 executes always, option 2 does too. But, if option 3 doesn't comform with the information in option 2,
it throws an error saying they aren't the same. Which means option 3 confirms option 2. If I'm wrong or there's anything to correct, please
do tell. So, I wrote something like this:
#Test
public void testBadHashException() throws Exception {
1. mRemoteRepository.getPopularMovies(1, FAKE_API_KEY).subscribeWith(mMovieListTestSubscriber);
2. mMovieListTestSubscriber.assertNoValues();
3. mMovieListTestSubscriber.assertError(HttpException.class);
}
This is what I noticed:
private List<Movie> mMovieList;
private DiscoverMoviesResponse mDiscoverMoviesResponse;
private MoviesRepository mRemoteRepository;
private TestObserver<List<Movie>> mMovieListTestSubscriber;
private TestObserver<Movie> mMovieTestSubscriber;
#Mock
MovieApiService apiService;
Those above, were declared at the top, and initialized by a Mockito #Before #annotation like this:
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mRemoteRepository = new MoviesRemoteRepository(apiService);
mMovieTestSubscriber = new TestObserver<>();
mMovieListTestSubscriber = new TestObserver<>();
mMovieList = TestDataGenerator.generateMovieList(10);
mDiscoverMoviesResponse = TestDataGenerator.generateDiscoverMoviesResponse(mMovieList);
}
Note: TestDataGenerator is a helper class for generating data. As it's done there, he got MovieList and then got another which is the main response body.
APIService: The retrofit service class.
MoviesRepository: An helper class for manipulating Observables in the service class. Which is used by the ViewModel.
The second test keeps giving me java.lang.RuntimeException: No mock defined for invocation. I don't seem to understand it yet.
Is there a specific instance where I should use when, verify, how do I test for Observable Retrofit Request Errors.
If it's saying no Mock data, but then Mock data has been generated when this is done. Or is it supposed to be mocked differently?
mMovieList = TestDataGenerator.generateMovieList(10);
mDiscoverMoviesResponse = TestDataGenerator.generateDiscoverMoviesResponse(mMovieList);
More on my observation:
I was going through Mockito and I noticed, the first test that went through was executing because he did:
1. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));
Since the error for the second function shows java.lang.RuntimeException: No mock defined for invocation, it was stated that the method
within a class can be mocked by using when("some method").thenReturn() it's okay. I then modified my testBadHashException to look like this:
#Test
public void testBadHashException() throws Exception {
0. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));
1. mRemoteRepository.getPopularMovies(1, FAKE_API_KEY).subscribeWith(mMovieListTestSubscriber);
2. mMovieListTestSubscriber.assertNoValues();
3. mMovieListTestSubscriber.assertError(HttpException.class);
}
Instead of it throwing an exception, it threw a success.
I rewrote the error test:
#Test
public void getPopularMoviesThrowsError() {
when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(Observable.<List<Movie>>error(new TimeoutException()));
// request movies
mMoviesViewModel.discoverMovies(true);
verify(mMoviesRepository).getPopularMovies(PAGE);
// check that empty view is hidden
assertFalse(mMoviesViewModel.emptyViewShowing.get());
// check that loading view is hidden
assertFalse(mMoviesViewModel.moviesLoading.get());
// check that error view is showing
assertTrue(mMoviesViewModel.errorViewShowing.get());
}
There is a compilation error here: when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(Observable.<List<Movie>>error(new TimeoutException()));
It cannot resolve method here: Observable.<List<Movie>>error(new TimeoutException())
Writing tests in Android looks really weird compared to JavaScript. Any help on how I can learn or achieve understanding how to write unit testing would be appreciated. I just adopted
the MVVM pattern and I'm trying to write test with it. Thanks.
If you need to send an error to your Observable you can create it like this:
when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(
Observable.create(new Observable.OnSubscribe<SomeClass>() {
#Override public void call(Subscriber<SomeCladd> subscriber) {
subscriber.onError(new Exception("some message!"));
}
}));
This way you will have a Observable that returns an error so you can call
mMovieListTestSubscriber.assertError
The problem that you are having is that you using Observable.just to create your Observable... So the method onError is never going to be called, just the onNext.

Categories

Resources