Unit Test - Verify Observable is subscribed

前端 未结 4 2100
攒了一身酷
攒了一身酷 2021-02-08 00:19

I have got the java code like this

 mDataManager.getObservable(\"hello\").subscribe( subscriber );

and I want to verify the follow

相关标签:
4条回答
  • 2021-02-08 00:53

    I'm not certain I am answering the asked question, but I think so...

    Unit tests can be created that check whether a subscription has been made to an observable. Note that in RxJava 1.x, this was supported with TestSubject, but that functionality can be facilitated by Subject implementations. TestSubject was dropped for RxJava 2.

    Rather than attempting to mock the observable as the original poster is trying to do, use a PublishSubject, run the business logic, then use the publish subject to see if the Observable is being observed. Below is some test code that validates this testing is possible. The tests pass when I run them.

    See code comments for description of the code. Personally, this test seems redundant to other tests that would verify how the controller handles data. Those other tests would fail if the controller never subscribed to and received data from the data service. On the other hand, testing unsubscribe helps avoid memory leaks, which is probably high value.

    import org.junit.Test;
    
    import io.reactivex.Observable;
    import io.reactivex.disposables.Disposable;
    import io.reactivex.subjects.PublishSubject;
    
    import static junit.framework.Assert.assertFalse;
    import static junit.framework.Assert.assertTrue;
    
    /**
     * Tests whether it is possible to test whether a controller properly unsubscribes from an Observable.
     */
    public class SubscribeUnsubscribeTests {
    
        /**
         * A service that returns a stream of strings.
         */
        interface StringService {
            Observable<String> getStrings();
        }
    
        /**
         * A controller that requests strings from the {@link StringService} and logs them
         * to system out as they are received.
         */
        class StringLoggingController {
    
            private StringService stringService;
    
            private Disposable stringSubscriptionDisposable;
    
            public StringLoggingController(StringService stringService) {
                this.stringService = stringService;
            }
    
            /**
             * Causese the controller to begin strings request and logging.
             */
            public void start() {
                stringSubscriptionDisposable = stringService.getStrings()
                        .subscribe(string -> System.out.print(string));
            }
    
            public void stop() {
                if (stringSubscriptionDisposable != null) {
                    if (!stringSubscriptionDisposable.isDisposed()) {
                        stringSubscriptionDisposable.dispose();
                        stringSubscriptionDisposable = null;
                    }
                }
            }
        }
    
        /**
         * A {@link StringService} that can report whether {@link StringService#getStrings()}
         * has observers.
         */
        class ReportIsSubscribedStringService implements StringService {
    
            private PublishSubject<String> publishSubject = PublishSubject.create();
    
            public Observable<String> getStrings() {
                return publishSubject;
            }
    
            /**
             * @return true if the {@link #getStrings()} observable has observers.
             */
            public boolean stringsAreBeingObserved() {
                return publishSubject.hasObservers();
            }
        }
    
        /**
         * Verifies that the {@link StringLoggingController} is subscribing to the service.
         */
        @Test
        public void stringsLoggingControllerSubscribesToStringService() {
            ReportIsSubscribedStringService service = new ReportIsSubscribedStringService();
            StringLoggingController controller = new StringLoggingController(service);
    
            controller.start();
            assertTrue(service.stringsAreBeingObserved());
        }
    
        /**
         * Verifies that the {@link StringLoggingController} is unsubscribing from the service.
         */
        @Test
        public void stringsLoggingControllerUnsubscribesFromStringService() {
            ReportIsSubscribedStringService service = new ReportIsSubscribedStringService();
            StringLoggingController controller = new StringLoggingController(service);
    
            controller.start();
            controller.stop();
    
            assertFalse(service.stringsAreBeingObserved());
        }
    }
    

    -- edit -- I previously wrote that I wasn't able to test for unsubscribe. I did figure out how to test that. It turns out my test was failing because the tested code wasn't properly unsubscribing (hah - testing works, go figure). I've updated the code above to illustrate testing both subscribe and unsubscribe.

    0 讨论(0)
  • 2021-02-08 00:57

    Maybe you could use Observable.onSubscribe method together with RunTestOnContext rule? The TestContext can provide you with an Async object, that terminates the test only once it is completed. I think, that if you combine this with Observable#doOnSubscribe you can achieve the desired behavior.

    However, using Async might be a bit confusing sometimes. In the example below, if the observable is never subscribed onto, the doOnSubscribe function would never get evaluated and your test would not terminate.

    Example:

    @RunWith(VertxUnitRunner.class)
    public class SubscriptionTest {
    
      @Rule
      public RunTestOnContext vertxRule = new RunTestOnContext();
    
      @Test
      public void observableMustBeSubscribed(final TestContext context) {
        final Async async = context.async();
        final Observable<String> observable = Observable.just("hello").doOnSubscribe(async::complete);
        final Manager mock = mock(Manager.class);
        when(mock.getObservable()).thenReturn(observable);
    
        mock.getObservable().subscribe();
      }
    
      interface Manager {
        Observable<String> getObservable();
      }
    }
    
    0 讨论(0)
  • 2021-02-08 01:03

    I found that RxJava provides a class called TestSubject

    You can create it like this

    private TestScheduler eventsScheduler = new TestScheduler();
    private TestSubject<MyEvent> eventObservable = TestSubject.create(eventsScheduler);
    

    This will provide you with the method hasObservers() which returns a boolean.

    @Test
    public void testSubscription(){
        myTestClass.init(eventObservable);
    
        assertTrue(eventObservable.hasObservers());
    }
    

    Also the TestSubject allows you to perfectly time when events should be sent.

    eventObservable.onNext(new MyEvent());
    eventsScheduler.triggerActions(); 
    
    0 讨论(0)
  • 2021-02-08 01:13

    I spent hours today and realize it was a silly mistake. Please check your @PrepareForTest if you are using PowerMockito.

    @PrepareForTest({Observable.class})
    

    also don't forget to mock it. I did it on @before :

    PowerMockito.mockStatic(Observable.class);
    

    I hope it can help. Thank you

    0 讨论(0)
提交回复
热议问题