2017-05-11 49 views
2

我是新的單元測試,我與rx機器人單元測試翻新。我有一個observable從api獲取訪問令牌,我使用它進行改進以發送請求。我得到空指針異常由於它在這裏是我的代碼:單元測試與翻新和Rx android

@RunWith(MockitoJUnitRunner.class) 
public class AuthenticationTokenGetterTest { 
    @Mock 
    AuthenticatorInterface authenticatorservice; 
    @InjectMocks 
    AuthenticationTokenGetter tokengetter; 

    @Test 
    public void testtokkengetter() { 
     when(authenticatorservice.servicecall(anyString(), anyString())).thenReturn(
       Observable.just("44fffffggggggg")); 

     Observable<String> obs = tokengetter.getToken(); 
     TestSubscriber<String> testsubscriber = new TestSubscriber<>(); 
     obs.subscribe(testsubscriber); 
     testsubscriber.assertNoErrors(); // Here I get exception 
     List<String> value = testsubscriber.getOnNextEvents(); 
    } 

} 

但我總是得到java.lang.NullPointerException。 和我觀察到的代碼是我測試:

@CheckResult 
    public Observable<String> getToken() { 
     return service.servicecall(key, code) 
       .subscribeOn(Schedulers.newThread()) 
       .doOnNext(new Action1<String>() { 
        public void call(String token) { 
         savedToken = token; 
        } 
       }) 
       .observeOn(AndroidSchedulers.mainThread()); 
    } 

我的錯誤是:

java.lang.AssertionError: Unexpected onError events: 1 

    at rx.observers.TestSubscriber.assertNoErrors(TestSubscriber.java:308) 
    at AuthenticationTokenGetterTest.testtokkengetter(AuthenticationTokenGetterTest.java:47) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 
    at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37) 
    at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62) 
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137) 
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117) 
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) 
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262) 
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 
Caused by: java.lang.NullPointerException 
    at rx.android.schedulers.LooperScheduler$HandlerWorker.schedule(LooperScheduler.java:77) 
    at rx.android.schedulers.LooperScheduler$HandlerWorker.schedule(LooperScheduler.java:91) 
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.schedule(OperatorObserveOn.java:190) 
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$1.request(OperatorObserveOn.java:147) 
    at rx.Subscriber.setProducer(Subscriber.java:209) 
    at rx.Subscriber.setProducer(Subscriber.java:205) 
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.init(OperatorObserveOn.java:141) 
    at rx.internal.operators.OperatorObserveOn.call(OperatorObserveOn.java:75) 
    at rx.internal.operators.OperatorObserveOn.call(OperatorObserveOn.java:40) 
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:46) 
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) 
    at rx.Observable.subscribe(Observable.java:8759) 
    at rx.Observable.subscribe(Observable.java:8726) 
.AuthenticationTokenGetterTest.testtokkengetter(AuthenticationTokenGetterTest.java:45) 
+0

請提供堆棧跟蹤。 – jakubbialkowski

+0

@jakubbialkowski編輯 – SimpleMan

回答

3

你正在使用AndroidSchedulers.mainThread()這是依賴於Android的尺蠖類,這就是爲什麼空指針。不要創建使用多個線程的單元測試,在同一個線程上執行所有操作!

您可以通過執行調度程序注入來解決此問題。您的AuthenticationTokenGetter類應該獲得mainThreadScheduler實例Scheduler參考在構造函數中傳遞,因此在您的普通代碼中,您應該使用mainThreadScheduler創建對象,並在測試期間使用Scheduler實現同步執行所有操作。

您還可以使用RxJava/RxAndroidSchedulersHook來覆蓋調度程序。

@edit 有些文章解釋如何注入/ ovveride調度:

https://medium.com/@peter.tackage/overriding-rxandroid-schedulers-in-rxjava-2-5561b3d14212

https://medium.com/@peter.tackage/an-alternative-to-rxandroidplugins-and-rxjavaplugins-scheduler-injection-9831bbc3dfaf

這兩種解決方案都有它的優點和缺點,即使這些文章的目的是RxJava2這方法仍然適用於RxJava1(但是Rx2中的調度程序鉤子/插件的工作方式有點不同)

+0

謝謝你的回答。我理解你的答案的第一部分,但無法理解第二部分。我應該編輯我的普通代碼嗎? – SimpleMan

+0

@SimpleMan我已經添加了一些文章給我的答案,應該澄清一切 – Than

+0

非常感謝你,我的問題解決了。 – SimpleMan

0

我使用thi小號工藝:

  1. BaseSchedulerProvider(父親)
  2. ImmediateSchedulerProvider(測試)
  3. SchedulerProvider(應用)

BaseSchedulerProvider:

public interface BaseSchedulerProvider { 

    @NonNull 
    Scheduler computation(); 

    @NonNull 
    Scheduler io(); 

    @NonNull 
    Scheduler ui(); 
} 

ImmediateSchedulerProvider我用了一個測試:

public class ImmediateSchedulerProvider implements BaseSchedulerProvider { 

    @NonNull 
    @Override 
    public Scheduler computation() { 
     return Schedulers.immediate(); 
    } 

    @NonNull 
    @Override 
    public Scheduler io() { 
     return Schedulers.immediate(); 
    } 

    @NonNull 
    @Override 
    public Scheduler ui() { 
     return Schedulers.immediate(); 
    } 
} 

而且SchedulerProvider我在演示

public class SchedulerProvider implements BaseSchedulerProvider { 

    // Prevent direct instantiation. 
    public SchedulerProvider() { 
    } 

    @Override 
    @NonNull 
    public Scheduler computation() { 
     return Schedulers.computation(); 
    } 

    @Override 
    @NonNull 
    public Scheduler io() { 
     return Schedulers.io(); 
    } 

    @Override 
    @NonNull 
    public Scheduler ui() { 
     return AndroidSchedulers.mainThread(); 
    } 
} 

用在我的PresenterTest我安裝是這樣的:

public class TopicPresenterTest { 

      @Mock 
      private RemoteDataSource mRemoteDataSource; 

      @Mock 
      private TopicContract.View mView; 

      private BaseSchedulerProvider mSchedulerProvider; 

      TopicPresenter mPresenter; 

      List<Topics> mList; 

      @Before 
      public void setup() { 
      MockitoAnnotations.initMocks(this); 

        Topics topics = new Topics(1, "Discern The Beach"); 
        Topics topicsTwo = new Topics(2, "Discern The Football Player"); 
        mList = new ArrayList<>(); 
        mList.add(topics); 
        mList.add(topicsTwo); 
//ADD IMMEDIATESCHEDULERPROVIDER !!!!!!!!!!!!!!! 
        mSchedulerProvider = new 
        ImmediateSchedulerProvider(); 

        mPresenter = new TopicPresenter(mRemoteDataSource, mView, mSchedulerProvider); 

      } 

      @Test 
      public void fetchData() { 

       when(mRemoteDataSource.getTopicsRx()) 
         .thenReturn(rx.Observable.just(mList)); 

       mThemePresenter.fetch(); 

       InOrder inOrder = Mockito.inOrder(mView); 
       inOrder.verify(mView).setLoadingIndicator(false); 
       inOrder.verify(mView).showTopics(mList); 

      } 

} 

,在我的演講

public class TopicPresenter { 

     @NonNull 
     private BaseSchedulerProvider mSchedulerProvider; 

     public TopicPresenter(@NonNull RemoteDataSource remoteDataSource, @NonNull TopicContract.View view) { 
        this.mRemoteDataSource = checkNotNull(remoteDataSource, "remoteDataSource"); 
        this.mView = checkNotNull(view, "view cannot be null!"); 
        this.mSchedulerProvider = new SchedulerProvider(); 
//ADD COMPOSITESUBSCRITPTION !!!!!!  
        mSubscriptions = new CompositeSubscription(); 

        mView.setPresenter(this); 
     } 
} 

您可以檢查my complete example in GitHubthis article.