Search code examples
unit-testingtestingrx-java2

Unit Testing RxJava doOnSubscribe and doFinally


How do I create a unit test that a certain side effect is done on doOnScubscribe and doFinally of an rxjava chain?

For Example:

Observable.requestSomeValueFromWeb()
            .doOnSubscribe(() -> showLoading = true)
            .doFinally(() -> showLoading = false)
            .subscribe(result -> doSomething(result), error -> doErrorHandling(error));

How do I test in the scenario above that the showLoading was set to true on subscribe and false when the observable was disposed?

TestSubscriber<WebServiceResponse> loginRequestSubscriber = new TestSubscriber<>();

clientLoginViewModel.requestLogin().subscribe(loginRequestSubscriber);

// check that showLoading was true when webservice was called
assertEquals(true, showLoading);

// check that showLoading was false when webservice was finished
assertEquals(false, showLoading);

loginRequestSubscriber.assertSubscribed();

Solution

  • If I understand correctly, your Object Under Test is the ClientLoginViewModel, so I'm trying to work from there. Let me know if I'm mistaken and I can revisit my answer:

    The classes from your system:

    interface WebServiceResponse { } // We don't care about this here
    
    interface Network {
        // This is whatever interacts with the Network, and we'll mock it out
        Single<WebServiceResponse> requestSomeValue();
    }
    
    // The class we are testing
    class ClientLoginViewModel {
    
        final Network mNetwork;
    
        // This is the field we want to check... you probably want to make it
        // private and have accessors for it
        boolean showLoading;
    
        // This allows dependency injection, so we can mock :)
        ClientLoginViewModel(final Network network) {
            mNetwork = network;
        }
    
        // The actual method to test!
        Single<WebServiceResponse> requestLogin() {
           return mNetwork.requestSomeValue()
               .doOnSubscribe(disposable -> showLoading = true)
               .doFinally(() -> showLoading = false);
        }
    }
    

    And now the test!!!

    class ClientLoginViewModelTest {
    
        @Test
        public void testLoading() {
            final Network network = mock(Network.class);
            final WebServiceResponse response = mock(WebServiceResponse.class);
    
            // This is the trick! We'll use it to allow us to assert anything
            // before the stream is done
            final PublishSubject<Boolean> delayer = PublishSubject.create();
            when(network.requestSomeValue())
                .thenReturn(
                   
     Single.just(response).delaySubscription(publishSubject)
                );
    
            final ClientLoginViewModel clientLoginViewModel = new ClientLoginViewModel(network);
    
            clientLoginViewModel
                .requestLogin()
                .test();
    
            // check that showLoading was true when webservice was called
            assertEquals(true, clientLoginViewModel.showLoading);
    
            // now let the response from the Network continue
            publishSubject.onComplete();
    
            // check that showLoading was false when webservice was finished
            assertEquals(false, clientLoginViewModel.showLoading);
        }
    }