Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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...

like image 245
greysqrl Avatar asked Jan 30 '23 01:01

greysqrl


2 Answers

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);
}
like image 140
GVillani82 Avatar answered Feb 03 '23 07:02

GVillani82


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!

like image 25
cdehning Avatar answered Feb 03 '23 06:02

cdehning