2017-05-11 7 views
2

Ich bin neu in der Unit-Tests und ich bin Unit-Test Retrofit mit RX Android. Ich habe eine Observable, die Zugangstoken von der API erhält, und ich benutze es mit Retrofit, um Anfrage zu senden. Ich erhalte Null-Zeiger-Ausnahme auf Grund hier ist mein Code:Unit Testing mit Retrofit und 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(); 
    } 

} 

Aber ich bin immer java.lang.NullPointerException die ganze Zeit. und meine beobachtbaren Code ist, was ich testen:

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

Mein Fehler ist:

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

Bitte geben Sie auch den StackTrace an. – jakubbialkowski

+0

@jakubbialkowski bearbeitet – SimpleMan

Antwort

3

Ihre AndroidSchedulers.mainThread() verwenden, die auf Android Looper Klasse abhängig ist, das ist, warum Null-Zeiger. Erstellen Sie keine Komponententests, die mehrere Threads verwenden, führen Sie alles auf demselben Thread aus!

Sie können dies durch Scheduler-Injektion lösen. Ihre AuthenticationTokenGetter Klasse sollte erhalten mainThreadScheduler Instanz von Scheduler Referenz im Konstruktor übergeben, so in Ihrem normalen Code sollten Sie Ihr Objekt mit mainThreadScheduler erstellen und während des Tests erstellen Sie Ihr Objekt mit Scheduler Implementierung, die alles synchron ausführt.

Sie können auch RxJava/RxAndroidSchedulersHook zum Überschreiben von Schedulern verwenden.

@edit Einige Artikel erklärt, wie man/ovveride Disponenten zu injizieren:

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

Beide Lösungen hat seine Vorteile und Nachteile, auch wenn diese Artikel in RxJava2 dieser ausgerichtet sind Ansatz ist immer noch gültig mit RxJava1 (aber Scheduler Hooks/Plugins funktioniert ein wenig anders in Rx2)

+0

Vielen Dank für Ihre Antwort.Ich verstand Ihren ersten Teil der Antwort, konnte aber den zweiten Teil nicht verstehen. Soll ich auch meinen normalen Code bearbeiten? – SimpleMan

+0

@SimpleMan Ich habe meiner Antwort einige Artikel hinzugefügt, die alles klären sollten – Than

+0

Vielen Dank mein Problem ist gelöst. – SimpleMan

0

Ich benutze thi s Technik:

  1. BaseSchedulerProvider (Vater)
  2. ImmediateSchedulerProvider (Testing)
  3. SchedulerProvider (Application)

BaseSchedulerProvider:

public interface BaseSchedulerProvider { 

    @NonNull 
    Scheduler computation(); 

    @NonNull 
    Scheduler io(); 

    @NonNull 
    Scheduler ui(); 
} 

ImmediateSchedulerProvider ich für einen Test verwenden:

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

Und SchedulerProvider ich in meinem Presenter verwenden

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

In meinem PresenterTest ich setUp wie folgt aus:

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

      } 

} 

und in meinem Presenter

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

Sie können my complete example in GitHub überprüfen und this article.