使用EspressotestingRxJava2,并在suscribeOn时获得空指针exception

Android Studio 3.0 Beta2 

我正在testing使用RxJava2获取端点的列表。 该应用程序正常运行时工作正常。 但是,当我testing使用espresso时,我尝试和subscribeOn(scheduler)时,我得到一个空指针exception。 对于调度程序,我使用trampoline()来注入subscribeOnobserveOn

 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 

对于使用espressotestingRxJava2,有什么我应该做的,对于subscribeOnobserveOn是不同的?

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

这是我的课程

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

调度程序的界面在这里,为了testing我正在使用注入的蹦床

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

原型API的模拟模块

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

这是如何创build组件

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

对于expressotesting,我正在做以下工作:

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

非常感谢您的任何build议,

你的代码库有很多问题。 但首先是以下几点:你是以某种方式实例化新的真实对象(而不是嘲笑),这就是为什么你得到NPE,与subscribeOn()没有任何关系。

  1. 您应该使您的testing组件从您的生产组件扩展。 目前不是

     public interface TestRecipeListComponent extends RecipeListComponent {...} 
  2. 在您的testing应用程序类中,您正在混合callback,即您正在createApplicationComponentcallback中创buildTestRecipeListComponent ,但是您有另一个callback来执行此操作: createRecipeListComponent()

  3. 你不应该嘲笑你的MockRecipeListModule每一个东西。 只是嘲笑组件,你真的需要嘲笑。 例如,如果您模拟RecipeAdapter ,那么您如何期望回收者视图在屏幕上绘制任何东西? 你只需要嘲笑数据源提供者,在你的情况下是RecipeApi 。 除此之外什么都不应该被嘲笑,这不是一个unit testing,这是仪器testing。

  4. RecipeListView#onCreate()您正在创build一个新的RecipeListComponent ,而您应该从Application类获取该组件,因为您已经在那里创build了该组件。 这会影响testing:因为RecipeListView只会忽略所有你已经从testing中改变的依赖关系,并且会创build一个新的组件来提供其他的依赖关系,所以你的存根将不会返回你明确指定的数据在testing中硬编码(实际上甚至不会被称为真实对象)。 这正是你遇到的问题。

我已经解决了这一切。 我已经到了你写的断言没有通过的地步。 您应该继续这个麻烦,因为它与您正在使用的逻辑/体系结构相关联。

我在这里打开了一个请求。