8
Android Studio 3.0 Beta2 

RxJava2を使用してエンドポイントのリストを取得するテストを行っています。正常に動作していればアプリは正常に動作します。しかし、私がエスプレッソを使ってテストすると、私が試してみるとnullポインタ例外が発生します。subscribeOn(scheduler)。スケジューラの場合、私はsubscribeOnobserveOnの両方に対してtrampoline()を使用しています。エスプレッソを使用してRxJava2をテストするためのエスプレッソを使用してRxJava2をテストし、suscribeOn時にnullポインタ例外が発生する

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'io.reactivex.Observable io.reactivex.Observable.subscribeOn(io.reactivex.Scheduler)' on a null object reference 

私はそれを行う必要があるものがありsubscribeOnobserveOnに異なるのですか?

@Singleton 
@Component(modules = { 
     MockNetworkModule.class, 
     MockAndroidModule.class, 
     MockExoPlayerModule.class 
}) 
public interface TestBusbyBakingComponent extends BusbyBakingComponent { 
    TestRecipeListComponent add(MockRecipeListModule mockRecipeListModule); 
} 

これは、スケジューラ用のインターフェースはこことテストのために、私はRecipleAPI

ため

@Module 
public class MockAndroidModule { 
    @Singleton 
    @Provides 
    Context providesContext() { 
     return Mockito.mock(Context.class); 
    } 

    @Singleton 
    @Provides 
    Resources providesResources() { 
     return Mockito.mock(Resources.class); 
    } 

    @Singleton 
    @Provides 
    SharedPreferences providesSharedPreferences() { 
     return Mockito.mock(SharedPreferences.class); 
    } 

    @Singleton 
    @Provides 
    RecipeSchedulers provideRecipeSchedulers() { 
     return new RecipeSchedulers() { 
      @Override 
      public Scheduler getBackgroundScheduler() { 
       return Schedulers.trampoline(); 
      } 

      @Override 
      public Scheduler getUIScheduler() { 
       return Schedulers.trampoline(); 
      } 
     }; 
    } 
} 

モックモジュールを注入されたトランポリンを使用していますされ、テスト中の

public class RecipeListModelImp 
     implements RecipeListModelContract { 

    private RecipesAPI recipesAPI; 
    private RecipeSchedulers recipeSchedulers; 
    private CompositeDisposable compositeDisposable = new CompositeDisposable(); 

    @Inject 
    public RecipeListModelImp(@NonNull RecipesAPI recipesAPI, @NonNull RecipeSchedulers recipeSchedulers) { 
     this.recipesAPI = Preconditions.checkNotNull(recipesAPI); 
     this.recipeSchedulers = Preconditions.checkNotNull(recipeSchedulers); 
    } 

    @Override 
    public void getRecipesFromAPI(final RecipeGetAllListener recipeGetAllListener) { 
     compositeDisposable.add(recipesAPI.getAllRecipes() 
       .subscribeOn(recipeSchedulers.getBackgroundScheduler()) /* NULLPOINTER EXCEPTION HERE */ 
       .observeOn(recipeSchedulers.getUIScheduler()) 
       .subscribeWith(new DisposableObserver<List<Recipe>>() { 
        @Override 
        protected void onStart() {} 

        @Override 
        public void onNext(@io.reactivex.annotations.NonNull List<Recipe> recipeList) { 
         recipeGetAllListener.onRecipeGetAllSuccess(recipeList); 
        } 

        @Override 
        public void onError(Throwable e) { 
         recipeGetAllListener.onRecipeGetAllFailure(e.getMessage()); 
        } 

        @Override 
        public void onComplete() {} 
       })); 
    } 

    @Override 
    public void releaseResources() { 
     if(compositeDisposable != null && !compositeDisposable.isDisposed()) { 
      compositeDisposable.clear(); 
      compositeDisposable.dispose(); 
     } 
    } 
} 

私のクラスであります

@Module 
public class MockNetworkModule { 
    @Singleton 
    @Provides 
    public RecipesAPI providesRecipeAPI() { 
     return Mockito.mock(RecipesAPI.class); 
    } 
} 

これは、コンポーネントが

public class TestBusbyBakingApplication extends BusbyBakingApplication { 
    private TestBusbyBakingComponent testBusbyBakingComponent; 
    private TestRecipeListComponent testRecipeListComponent; 

    @Override 
    public TestBusbyBakingComponent createApplicationComponent() { 
     testBusbyBakingComponent = createTestBusbyBakingComponent(); 
     testRecipeListComponent = createTestRecipeListComponent(); 

     return testBusbyBakingComponent; 
    } 

    private TestBusbyBakingComponent createTestBusbyBakingComponent() { 
     testBusbyBakingComponent = DaggerTestBusbyBakingComponent.builder() 
       .build(); 

     return testBusbyBakingComponent; 
    } 

    private TestRecipeListComponent createTestRecipeListComponent() { 
     testRecipeListComponent = testBusbyBakingComponent.add(new MockRecipeListModule()); 
     return testRecipeListComponent; 
    } 
} 

を作成され、エスプレッソのテストのために、私は次のことをやっている方法です:

@RunWith(MockitoJUnitRunner.class) 
public class RecipeListViewAndroidTest { 
    @Inject RecipesAPI recipesAPI; 

    @Mock RecipeListModelContract.RecipeGetAllListener mockRecipeListener; 

    @Rule 
    public ActivityTestRule<MainActivity> mainActivity = 
      new ActivityTestRule<>(
        MainActivity.class, 
        true, 
        false); 

    @Before 
    public void setup() throws Exception { 
     Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 
     BusbyBakingApplication busbyBakingApplication = 
       (BusbyBakingApplication)instrumentation.getTargetContext().getApplicationContext(); 

     TestBusbyBakingComponent component = (TestBusbyBakingComponent)busbyBakingApplication.createApplicationComponent(); 
     component.add(new MockRecipeListModule()).inject(this); 
    } 

    @Test 
    public void shouldReturnAListOfRecipes() throws Exception { 
     List<Recipe> recipeList = new ArrayList<>(); 
     Recipe recipe = new Recipe(); 
     recipe.setName("Test Brownies"); 
     recipe.setServings(10); 
     recipeList.add(recipe); 

     when(recipesAPI.getAllRecipes()).thenReturn(Observable.just(recipeList)); 
     doNothing().when(mockRecipeListener).onRecipeGetAllSuccess(recipeList); 

     mainActivity.launchActivity(new Intent()); 

     onView(withId(R.id.rvRecipeList)).check(matches(hasDescendant(withText("Test Brownies")))); 
    } 
} 

スタックトレース:任意の提案のための

at me.androidbox.busbybaking.recipieslist.RecipeListModelImp.getRecipesFromAPI(RecipeListModelImp.java:37) 
at me.androidbox.busbybaking.recipieslist.RecipeListPresenterImp.retrieveAllRecipes(RecipeListPresenterImp.java:32) 
at me.androidbox.busbybaking.recipieslist.RecipeListView.getAllRecipes(RecipeListView.java:99) 
at me.androidbox.busbybaking.recipieslist.RecipeListView.onCreateView(RecipeListView.java:80) 
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2192) 
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1299) 
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1528) 
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1595) 
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:758) 
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2363) 
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2149) 
at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2103) 
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2013) 
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:388) 
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:607) 
at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178) 
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1237) 
at android.support.test.runner.MonitoringInstrumentation.callActivityOnStart(MonitoringInstrumentation.java:544) 
at android.app.Activity.performStart(Activity.java:6268) 

多くのおかげで、

+1

あなたは例外のスタックトレースを投稿できますか?また、MockRecipeSchedulersModuleはどこに注入されていますか?私はMockRecipeListModuleが注入されるのを見るだけです。 – jdonmoyer

+0

@jdonmoyer申し訳ありませんが、間違ったモジュールが含まれています。そのスケジューラーを返すプロバイダを含むMockAndroidModule(私は自分の質問を更新しました)と呼ばれます。彼らが注入される方法は、コンストラクタ注入を使用しています。注入 public RecipeListModelImp(NonNull RecipesAPI recipesAPI、NonNull RecipeSchedulers recipeSchedulers)。私は今日まで家に帰るまで、スタックトレースを提供することはできません。ありがとう – ant2009

+1

RecipesAPIのテスト実装はどこで提供しますか?私はそれがRecipeListViewAndroidTestに注入され、 'Mock'型のものであることだけがわかりますか?さらに、 'TestBusbyBakingComponent'に' MockAndroidModule'が追加されているところはありません。これ以上詳しく教えていただけますか? –

答えて

6

コードベースには多くの問題があります。しかし、まず第一に、次のようなことです:あなたは何らかの形で新しい実際のオブジェクト(モックではない)をインスタンス化しているので、NPEを取得しています。subscribeOn()とは関係ありません。

  1. 生産コンポーネントからテストコンポーネントを拡張する必要があります。現在はではなく、です。テストアプリケーションクラスで

    public interface TestRecipeListComponent extends RecipeListComponent {...} 
    
  2. あなたはコールバックを混合している、すなわち、あなたはTestRecipeListComponentcreateApplicationComponent内でコールバックを作成しているが、あなたはそれを行うための別のコールバックを持っている:createRecipeListComponent()を。

  3. あなたはではありません。は、すべてあなたのMockRecipeListModuleのすべてを模倣してください。あなたが本当にモックアウトする必要があるコンポーネントを模倣してください。たとえば、RecipeAdapterをモックした場合、リサイクラビューで画面に何かを描画するにはどうすればよいでしょうか?あなたはちょうどRecipeApiであるデータソースプロバイダを模倣する必要があります。それ以外は何も嘲笑されるべきではありません。これは単体テストではありません。これは計装テストです。RecipeListView#onCreate()

  4. あなたはすでにそこにそれを作成しているので、あなたがない、あなたは、Applicationクラスからそのコンポーネントを取得する必要がありますする必要があり、一方、あなたは、新しいRecipeListComponentを作成しています。 RecipeListViewは、テストから変更したすべての依存関係を無視し、他の依存関係を提供する新しいコンポーネントを作成するため、スタブはではないはあなたのデータを返すので、依存関係をそこから制御することはできません。テストで明示的にハードコードされています(実際には呼び出されず、実際のオブジェクトもあります)。これはまさにあなたが問題を経験していたものです。

私はこれをすべて修正しました。私は、あなたが書いた主張が合格しないところまで来た。あなたが使用しているロジック/アーキテクチャに接続されているので、これを続行するには面倒を取る必要があります。

プルリクエストhereを開設しました。

+0

素晴らしい仕事。私はコードをチェックし、すべてがうまくいった。私はTestBusbyBakingApplicationで少しのリファクタリングをしなければなりませんでした。私はここでPRを作成しました:https://github.com/azizbekian/BusbyBaking/pull/1。しかし、これは私の質問に答えました。まことにありがとうございます。 – ant2009

関連する問題