简体   繁体   中英

Android Unit testing run all class tests fail but every single method run successfully

please help me this when i run all the class test methods together the test fails in a method although this method when i run it alone it succeed

public class RealEstatesListPresenterTest {
RealEstatesListPresenter mRealEstatesListPresenter;
@Mock
private RealEstateListBusiness mRealEstateListBusiness;
@Mock
private RealEstatesListContract.View mRealEstatesView;

@BeforeClass
public static void setUpClass() {
    RxAndroidPlugins.setInitMainThreadSchedulerHandler(__ -> Schedulers.trampoline());
}

@Before
public void setupTasksPresenter() {
    MockitoAnnotations.initMocks(this);
    mRealEstatesListPresenter = new RealEstatesListPresenter(mRealEstatesView);
    mRealEstatesListPresenter.setmRealEstateListBusiness(mRealEstateListBusiness);
}

@Test
public void testWhenGetAllRealEstates_ProgressISDisplayed() {
    when(mRealEstateListBusiness.getAllRealEstates()).thenReturn(Observable.create(sub -> {
        sub.onNext(new ArrayList<>());
        sub.onComplete();
    }));
    mRealEstatesListPresenter.getAllRealEstates();
    verify(mRealEstatesView, times(1)).showLoading();
}

@Test
public void testWhenGetAllRealEstatesSuccess_ProgressISHidden() {
    when(mRealEstateListBusiness.getAllRealEstates()).thenReturn(Observable.create(sub -> {
        sub.onNext(new ArrayList<>());
        sub.onComplete();
    }));
    mRealEstatesListPresenter.getAllRealEstates();
    verify(mRealEstatesView, times(1)).hideLoading();
}

@Test
public void testWhenGetAllRealEstatesError_ProgressISHidden() {
    when(mRealEstateListBusiness.getAllRealEstates()).thenReturn(Observable.create(sub -> {
        sub.onError(new Throwable());
    }));
    mRealEstatesListPresenter.getAllRealEstates();
    verify(mRealEstatesView, times(1)).hideLoading();
}

@AfterClass
public static void tearDownClass() {
    RxAndroidPlugins.reset();
}}

when i run all the tests together the first two methods pass but the last one fail (testWhenGetAllRealEstatesError_ProgressISHidden) but when i run it alone it pass.

and this is the presenter code

public class RealEstatesListPresenter implements RealEstatesListContract.Presenter {

private RealEstatesListContract.View mView;
private RealEstateListBusiness mRealEstateListBusiness;
private CompositeDisposable mSubscriptions;

@Inject
public RealEstatesListPresenter(RealEstatesListContract.View view) {
    this.mView = view;
    mSubscriptions = new CompositeDisposable();
}

@Inject
public void setmRealEstateListBusiness(RealEstateListBusiness mRealEstateListBusiness) {
    this.mRealEstateListBusiness = mRealEstateListBusiness;
}

@Inject
public void setupListeners() {
    mView.setPresenter(this);
}

@Override
public void unSubscribe() {
    mSubscriptions.clear();
}

@Override
public void getAllRealEstates() {
    mView.showLoading();
    mSubscriptions.add(mRealEstateListBusiness.getAllRealEstates().observeOn(AndroidSchedulers.
            mainThread()).subscribeOn(Schedulers.io()).subscribe((realEstatesItems) -> {
        mView.hideLoading();
        mView.showAllRealEstates(realEstatesItems);
    }, throwable -> {
        mView.hideLoading();
        mView.showErrorMessage(throwable.getMessage());
    }));
}

}

i found the problem it is in RX Scheulers This error occurs because the default scheduler returned by AndroidSchedulers.mainThread() is an instance of LooperScheduler and relies on Android dependencies that are not available in JUnit tests.

We can avoid this issue by initializing RxAndroidPlugins with a different Scheduler before the tests are run. You can do this inside of a @BeforeClass so the final class would be like this

public class RealEstatesListPresenterTest {
private RealEstatesListPresenter mRealEstatesListPresenter;
@Mock
private RealEstateListBusiness mRealEstateListBusiness;
@Mock
private RealEstatesListContract.View mRealEstatesView;

@BeforeClass
public static void setUpClass() {
    Scheduler immediate = new Scheduler() {
        @Override
        public Worker createWorker() {
            return new ExecutorScheduler.ExecutorWorker(Runnable::run);
        }
    };

    RxJavaPlugins.setInitIoSchedulerHandler(scheduler -> immediate);
    RxJavaPlugins.setInitComputationSchedulerHandler(scheduler -> immediate);
    RxJavaPlugins.setInitNewThreadSchedulerHandler(scheduler -> immediate);
    RxJavaPlugins.setInitSingleSchedulerHandler(scheduler -> immediate);
    RxAndroidPlugins.setInitMainThreadSchedulerHandler(scheduler -> immediate);    }

@Before
public void setupTasksPresenter() {
    MockitoAnnotations.initMocks(this);
    mRealEstatesListPresenter = new RealEstatesListPresenter(mRealEstatesView);
    mRealEstatesListPresenter.setmRealEstateListBusiness(mRealEstateListBusiness);
}

@Test
public void testWhenGetAllRealEstates_ProgressISDisplayed() {
    when(mRealEstateListBusiness.getAllRealEstates()).thenReturn(Observable.create(sub -> {
        sub.onNext(new ArrayList<>());
        sub.onComplete();
    }));
    mRealEstatesListPresenter.getAllRealEstates();
    verify(mRealEstatesView, times(1)).showLoading();
}

@Test
public void testWhenGetAllRealEstatesSuccess_ProgressISHidden() {
    when(mRealEstateListBusiness.getAllRealEstates()).thenReturn(Observable.create(sub -> {
        sub.onNext(new ArrayList<>());
        sub.onComplete();
    }));
    mRealEstatesListPresenter.getAllRealEstates();
    verify(mRealEstatesView, times(1)).hideLoading();
}

@Test
public void testWhenGetAllRealEstatesError_ProgressISHidden() {
    when(mRealEstateListBusiness.getAllRealEstates()).thenReturn(Observable.create(sub -> {
        sub.onError(new Throwable());
    }));
    mRealEstatesListPresenter.getAllRealEstates();
    verify(mRealEstatesView, times(1)).hideLoading();
}

@Test
public void testWhenGetAllRealEstatesError_ErrorMessageDisplayed() {
    when(mRealEstateListBusiness.getAllRealEstates()).thenReturn(Observable.create(sub -> {
        sub.onError(new Throwable(""));
    }));
    mRealEstatesListPresenter.getAllRealEstates();
    verify(mRealEstatesView, times(1)).showErrorMessage("");
}

@After
public void teardown() {
    Mockito.reset(mRealEstateListBusiness);
    Mockito.reset(mRealEstatesView);
    Mockito.validateMockitoUsage();
}

@AfterClass
public static void tearDownClass() {
    RxJavaPlugins.reset();
    RxAndroidPlugins.reset();    }}

I'm not sure about the code itself But If each test run successfully if your run it only and fails if it's running with other tests

Then a test case may make corruptions for other test case if they are sharing some data, running in the same context or accessing the same db, cache, file or any repository each test case should run successfully with no dependence on other test case

As you're using Mockito, It seems it's the issue of its context you should reset mocked objects on each test case (ie in @Before) Mockito provides a reset method Mockito.reset(mRealEstatesView);

The issue is that mockito counts all the hits on that object across all the test cases, So you should reset it before each test case

You can use Android Test Orchestrator. As you can read here

When using AndroidJUnitRunner version 1.0 or higher, you have access to a tool called Android Test Orchestrator, which allows you to run each of your app's tests within its own invocation of Instrumentation.

To add it to your project:

  • add dependency to build.gradle (:app) file:

androidTestUtil "androidx.test:orchestrator:1.2.0"

  • add following code to your testoptions:

testOptions { execution 'ANDROIDX_TEST_ORCHESTRATOR' }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM