Android Studio 3.0 Beta2
Testuję pobieranie listy dla punktu końcowego za pomocą RxJava2. Aplikacja działa poprawnie, gdy działa normalnie. Jednak gdy testuję przy użyciu espresso, otrzymuję wyjątek wskaźnika pustego, gdy próbuję i subscribeOn(scheduler)
. Dla planistów używam trampoline()
zarówno dla subscribeOn
i observeOn
, które są wstrzykiwane.Testowanie RxJava2 przy użyciu Espresso i uzyskiwanie wyjątku wskaźnika pustego, gdy suscribeOn
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
Do testowania RxJava2 używając espresso jest coś zrobić, że jest inna dla subscribeOn
i observeOn
?
@Singleton
@Component(modules = {
MockNetworkModule.class,
MockAndroidModule.class,
MockExoPlayerModule.class
})
public interface TestBusbyBakingComponent extends BusbyBakingComponent {
TestRecipeListComponent add(MockRecipeListModule mockRecipeListModule);
}
To moja klasa badanego
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();
}
}
}
Interfejs dla planistów jest tu i do testów używam trampolina, który jest wtryskiwany
@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();
}
};
}
}
Mock moduł do RecipleAPI
@Module
public class MockNetworkModule {
@Singleton
@Provides
public RecipesAPI providesRecipeAPI() {
return Mockito.mock(RecipesAPI.class);
}
}
W ten sposób elementy są tworzone
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;
}
}
A do testu expresso robie następujące: trace
@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"))));
}
}
stosu:
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)
Wielkie dzięki za wszelkie sugestie,
Czy możesz opublikować ślad stosu wyjątku, który otrzymujesz? Ponadto, gdzie jest wstrzykiwany MockRecipeSchedulersModule? Widzę tylko wstrzyknięcie MockRecipeListModule. – jdonmoyer
@jdonmoyer Przepraszamy, dodałem zły moduł. Jest nazywany MockAndroidModule, który zawiera dostawców, którzy zwracają harmonogramy (zaktualizowałem moje pytanie). Sposób ich wstrzykiwania polega na użyciu wstrzyknięcia konstruktora. Inject public RecipeListModelImp (NonNull RecipesAPI recipesAPI, NonNull RecipeSchedulers recipeSchedulers). Nie mogę dostarczyć śladu stosu, dopóki nie wrócę do domu później. Dzięki – ant2009
Gdzie zapewniasz testową implementację RecipesAPI? Widzę tylko, że jest wstrzyknięty do RecipeListViewAndroidTest i ma typ "Mock", prawda? Ponadto nie widzę, gdzie 'MockAndroidModule' jest dodawany do' TestBusbyBakingComponent'. Czy mógłbyś bardziej szczegółowo to wyjaśnić? –