IdlingResource Espresso with RxJava

2019-03-12 20:15发布

问题:

I recently converted my application from using async tasks to rxjava. Now, my espresso tests no longer wait for my data calls to complete due to espresso not having an idling resources for rxjava. I noticed that you can make custom idling resources but I can't figure out how to make it work with rxJava Schedulers, Scheduler.io specifically. Any help/best practice would be greatly appreciated.

回答1:

Here is how I solved the problem:

IdlingResource implementation:

public class IdlingApiServiceWrapper implements MyRestService, IdlingResource {

private final MyRestService           api;
private final AtomicInteger counter;
private final List<ResourceCallback> callbacks;

public IdlingApiServiceWrapper(MyRestService api) {
    this.api = api;
    this.callbacks = new ArrayList<>();
    this.counter = new AtomicInteger(0);
}

public Observable<MyData> loadData(){
    counter.incrementAndGet();
    return api.loadData().finallyDo(new Action0() {
        @Override
        public void call() {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    counter.decrementAndGet();
                    notifyIdle();
                }
            });
        }
    });
}

@Override public String getName() {
    return this.getClass().getName();
}

@Override public boolean isIdleNow() {
    return counter.get() == 0;
}

@Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
    callbacks.add(resourceCallback);
}

private void notifyIdle() {
    if (counter.get() == 0) {
        for (ResourceCallback cb : callbacks) {
            cb.onTransitionToIdle();
        }
    }
   }
   }

and here is my test:

public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity> {
@Inject
IdlingApiServiceWrapper idlingApiWrapper;

@Override
public void setUp() throws Exception {
    //object graph creation

    super.setUp();
    getActivity();
    Espresso.registerIdlingResources(idlingApiWrapper);
}

public void testClickOpenFirstSavedOffer() throws Exception {
    onData(is(instanceOf(DataItem.class)))
            .atPosition(0)
            .perform(click());
   }
  }

I used Dagger for dependency injection.



回答2:

Wrote a little integration piece between RxJava Plugins and Espresso. Hope this helps someone else.

https://gist.github.com/digitalbuddha/d886eae1578bca78b9bf

Edit:

There is a much easier way to accomplish this task. Add the following rule to your tests

public class AsyncTaskSchedulerRule implements TestRule {
    final Scheduler asyncTaskScheduler = Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR);

    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                RxJavaHooks.setOnIOScheduler(scheduler -> asyncTaskScheduler);
                RxJavaHooks.setOnComputationScheduler(scheduler -> asyncTaskScheduler);
                RxJavaHooks.setOnNewThreadScheduler(scheduler -> asyncTaskScheduler);
                try {
                    base.evaluate();
                } finally {
                    RxJavaHooks.reset();
                }
            }
        };
    }
}


回答3:

I am currently using this implementation. Its easier and works very well for me so far: https://github.com/rosshambrick/RxEspresso