简体   繁体   English

单元测试 - 已订阅验证Observable

[英]Unit Test - Verify Observable is subscribed

I have got the java code like this 我有这样的java代码

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

and I want to verify the following Observable is being .subscribe() 我想verify以下Observable是.subscribe()

I have tried to mock getObservable() and verify 我试图模拟getObservable()verify

 Observable<Response> res = mock(Observable.class);
 when(mDataManager.getObservable("hello")).thenReturn(res);
 verify(res).subscribe();

But there is an error 但是有一个错误

Caused by: java.lang.IllegalStateException: onSubscribe function can not be null.
at rx.Observable.subscribe(Observable.java:8167)
at rx.Observable.subscribe(Observable.java:8158)
at rx.Observable.subscribe(Observable.java:7962)
....
Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: omni.neo.hk.omniapiservice.v4.model.external.UserLoginBean.class
at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:109)
at rx.exceptions.Exceptions.throwOrReport(Exceptions.java:187)
at rx.internal.operators.OperatorDoOnEach$1.onNext(OperatorDoOnEach.java:82)
... 48 more

I think it is not possible mock an Observable here, but without an mocked Observable I cannot do verify(res).subscribe() 我认为这里不可能mock一个Observable,但没有一个mock的Observable我不能做verify(res).subscribe()

Any suggestion in this case? 在这种情况下的任何建议?

I found that RxJava provides a class called TestSubject 我发现RxJava提供了一个名为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. 这将为您提供返回布尔值的方法hasObservers()

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

    assertTrue(eventObservable.hasObservers());
}

Also the TestSubject allows you to perfectly time when events should be sent. 此外,TestSubject还可以让您在发送事件时获得完美的时间。

eventObservable.onNext(new MyEvent());
eventsScheduler.triggerActions(); 

Maybe you could use Observable.onSubscribe method together with RunTestOnContext rule? 也许您可以将Observable.onSubscribe方法与RunTestOnContext规则一起使用? The TestContext can provide you with an Async object, that terminates the test only once it is completed. TestContext可以为您提供Async对象,只有在完成后才会终止测试。 I think, that if you combine this with Observable#doOnSubscribe you can achieve the desired behavior. 我认为,如果将它与Observable#doOnSubscribe结合使用,您可以实现所需的行为。

However, using Async might be a bit confusing sometimes. 但是,使用Async有时可能会有点混乱。 In the example below, if the observable is never subscribed onto, the doOnSubscribe function would never get evaluated and your test would not terminate. 在下面的示例中,如果从未订阅observable,则永远不会评估doOnSubscribe函数,并且您的测试不会终止。

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();
  }
}

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. 请注意,在RxJava 1.x中,TestSubject支持此功能,但Subject实现可以促进该功能。 TestSubject was dropped for RxJava 2 . 针对RxJava 2删除了TestSubject

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. 不要试图像原始海报那样试图模仿observable,而是使用PublishSubject,运行业务逻辑,然后使用发布主题查看是否正在观察Observable。 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. 我已经更新了上面的代码,以说明测试订阅和取消订阅。

I spent hours today and realize it was a silly mistake. 我今天花了几个小时意识到这是一个愚蠢的错误。 Please check your @PrepareForTest if you are using PowerMockito. 如果您使用的是PowerMockito,请检查您的@PrepareForTest。

@PrepareForTest({Observable.class})

also don't forget to mock it. 也别忘了嘲笑它。 I did it on @before : 我是在@before上做的:

PowerMockito.mockStatic(Observable.class);

I hope it can help. 我希望它可以提供帮助。 Thank you 谢谢

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM