I have a method that makes an API Call after 1 sec. I use Handler.postdelayed to implement this.
Now I am trying to verify if the API call is being made with a unit test.
#Mock
private PlanRepository planRepository;
#Mock
private CreatePlanContract.View view;
private CreatePlanContract.Presenter presenter;
#Captor
private ArgumentCaptor<ListResponseCallback<IntersectingList>> listCaptor;
....
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
presenter = new CreatePlanPresenter(resourceProvider, sessionContext, planRepository);
presenter.start();
presenter.setView(view);
...
}
#Test
public void testOnCameraIdleGetListEnabled(){
presenter.onCameraIdle(true);
verify(planRepository,times(1))
.getList(listCaptor.capture());
}
This is the method in presenter that I want to test:
class PlanPresenter implements PlanContract.Presenter{
private Handler mHandler = new Handler();
private Runnable mRunnable = this::fetchList;
private WeakReference<CreatePlanContract.View> createPlanView;
private ListResponseCallback<IntersectingList> listListener = new ListResponseCallback<Intersectinglist>() {
#Override
public void onSuccess(#NonNull List<IntersectingList> list) {
Log.d(TAG, "callback: success resp came");
if(createPlanView.get() != null)
createPlanView.get().renderList(list);
}
#Override
public void onError(int i, #NonNull String s, #NonNull APIResponseBody apiResponseBody, #Nullable Exception e) {
Log.d(TAG, "callback: error resp came Auth");
}
};
#Override
public void start() {
//some initilizations
}
#Override
public void setView(#NonNull CreatePlanContract.View view) {
this.createPlanView = new WeakReference<>(view);
}
#Override
public void onCameraIdle(){
mHandler.postDelayed(mRunnable,1000);
}
private void fetchList(){
//this the method to be verified
planRepository.getList(listListener);
}
}
But since the api call is being made after 1 sec, the test is failing.
What I have tried:
I tried following this link and use doAnswer() but I was unsuccessful.
I thought of using thread.sleep() which seems like an awful approach for this problem(also read that its a bad approach)
PS: I am a noob to testing.
I am using JUnit 4 and mockito
Try adding the following line, before calling the method from the presenter class
given(presenter.handler.postDelayed(any(Runnable.class), anyLong())).willReturn(true);
As an example
given(presenter.handler.postDelayed(any(Runnable.class), anyLong())).willReturn(true);
presenter.doSomething();
Related
I know it was asked before, but i am currently diving into testing and i have the struggle to unit test presenter in MVP pattern with Mockito
My code setup:
Item class
public class ItemJSON {
#SerializedName("title")
String textHolder;
#SerializedName("id")
int factNumber;
public ItemJSON(String factText, int factNumber) {
this.textHolder = factText;
this.factNumber = factNumber;
}
//getters and setters
}
Contractor:
public interface Contractor {
interface Presenter {
void getPosts();
}
interface View {
//parse data to recyclerview on Succesfull call.
void parseDataToRecyclerView(List<ItemJSON> listCall);
void onResponseFailure(Throwable throwable);
}
interface Interactor {
interface onGetPostsListener {
void onSuccessGetPostCall(List<ItemJSON> listCall);
void onFailure(Throwable t);
}
void getPosts(onGetPostsListener onGetPostsListener);
}
}
API class:
#GET("posts")
Call<List<ItemJSON>> getPost();
Interactor class:
public class InteractorImpl implements Contractor.Interactor{
#Override
public void getPosts(onGetPostsListener onGetPostsListener) {
// NetworkService responsible for seting up Retrofit2
NetworkService.getInstance().getJSONApi().getPost().enqueue(new Callback<List<ItemJSON>> () {
#Override
public void onResponse(#NonNull Call<List<ItemJSON>> call, #NonNull Response<List<ItemJSON>> response) {
Log.d("OPERATION #GET","CALLBACK SUCCESSFUL");
onGetPostsListener.onSuccessGetPostCall (response.body ());
}
#Override
public void onFailure(#NonNull Call<List<ItemJSON>>call, #NonNull Throwable t) {
Log.d("OPERATION #GET","CALLBACK FAILURE");
onGetPostsListener.onFailure (t);
}
});
}
Presenter class:
public class PresenterImpl implements Contractor.Presenter, Contractor.Interactor.onGetPostsListener {
private final Contractor.View view;
private final Contractor.Interactor interactor;
public PresenterImpl (Contractor.View view,Contractor.Interactor interactor){
this.view = view;
this.interactor = interactor;
}
#Override
public void getPosts() {
interactor.getPosts (this);
}
#Override
public void onSuccessGetPostCall(List<ItemJSON> listCall) {
view.parseDataToRecyclerView (listCall);
}
}
So i try to ran some unit test on presenter, but they constanlty fail and i keep getting next error
Wanted but not invoked Actually, there were zero interactions with this mock
Unit test class:
#RunWith (MockitoJUnitRunner.class)
public class ApiMockTest{
#Mock
Contractor.View view;
private PresenterImpl presenter;
#Captor
ArgumentCaptor<List<ItemJSON>> jsons;
#Before
public void setUp() {
MockitoAnnotations.openMocks (this);
presenter = new PresenterImpl (view,new InteractorImpl ());
}
#Test
public void loadPost() {
presenter.getPosts ();
verify(view).parseDataToRecyclerView (jsons.capture ());
Assert.assertEquals (2, jsons.capture ().size ());
}
}
I try to understand what i am doing wrong and how to fix this issue, but as for now i am ran out of ideas. I will aprecciate any help.
Thanks in the adavance
UPD: in all cases in main activity presenter get called in onClick
Main Activity class:
public class MainActivity extends AppCompatActivity implements Contractor.View {
public Contractor.Presenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
presenter = new PresenterImpl (this,new InteractorImpl ());
binding.getButton.setOnClickListener(view ->presenter.getPosts () );
...//code
#Override
public void parseDataToRecyclerView(List<ItemJSON> listCall) {
adapter.updateList(listCall); //diff call to put data into recyclerview adapter
}
}
}
I ran into this situation also, even using the mockk library. The problem is that your method is an interface method. You need to actually call it from a view which has implemented this interface.
I've up until yesterday successfully put together a very readable Android project using the MVP-pattern and the Android Annotations library.
But yesterday when I started writing unittest for my LoginPresenter a problem has shown itself.
First some code from my LoginPresenter.
...
#EBean
public class LoginPresenterImpl implements LoginPresenter, LoginInteractor.OnLoginFinishedListener {
#RootContext
protected LoginActivity loginView;
#Bean(LoginInteractorImpl.class)
LoginInteractor loginInteractor;
#Override public void validateCredentials(String username, String password) {
if (loginView != null) {
loginView.showProgress();
}
if (TextUtils.isEmpty(username)) {
// Check that username isn't empty
onUsernameError();
}
if (TextUtils.isEmpty(password)){
// Check that password isn't empty
onPasswordError();
// No reason to continue to do login
} else {
}
}
#UiThread(propagation = UiThread.Propagation.REUSE)
#Override public void onUsernameError() {
if (loginView != null) {
loginView.setUsernameError();
loginView.hideProgress();
}
}
...
My test:
#RunWith(MockitoJUnitRunner.class)
public class LoginPresenterImplTest {
private LoginPresenter loginPresenter;
#Mock
private LoginPresenter.View loginView;
#Before
public void setUp() {
// mock or create a Context object
Context context = new MockContext();
loginPresenter = LoginPresenterImpl_.getInstance_(context);
MockitoAnnotations.initMocks(this);
}
#After
public void tearDown() throws Exception {
loginPresenter = null;
}
#Test
public void whenUserNameIsEmptyShowUsernameError() throws Exception {
loginPresenter.validateCredentials("", "testtest");
// verify(loginPresenter).onUsernameError();
verify(loginView).setUsernameError();
}
}
The problem is I've not used the standard approach of using MVP-pattern but instead trying out Android Annotations to make the code more readable. So I've not used attachView()- or detachView()-methods for attaching my presenter to my LoginActivity (view). This means that I can't mock my "view". Does someone know a workaround for this problem. I keep getting following message when running the test:
Wanted but not invoked:
loginView.setUsernameError();
-> at com.conhea.smartgfr.login.LoginPresenterImplTest.whenUserNameIsEmptyShowUsernameError(LoginPresenterImplTest.java:48)
Actually, there were zero interactions with this mock.
Solution (I'm not using #RootContext anymore):
Presenter:
#EBean
public class LoginPresenterImpl extends AbstractPresenter<LoginPresenter.View>
implements LoginPresenter, LoginInteractor.OnLoginFinishedListener {
private static final String TAG = LoginPresenterImpl.class.getSimpleName();
#StringRes(R.string.activity_login_authenticating)
String mAuthenticatingString;
#StringRes(R.string.activity_login_aborting)
String mAbortingString;
#StringRes(R.string.activity_login_invalid_login)
String mInvalidCredentialsString;
#StringRes(R.string.activity_login_aborted)
String mAbortedString;
#Inject
LoginInteractor mLoginInteractor;
#Override
protected void initializeDagger() {
Log.d(TAG, "Initializing Dagger injection");
Log.d(TAG, "Application is :" + getApp().getClass().getSimpleName());
Log.d(TAG, "Component is: " + getApp().getComponent().getClass().getSimpleName());
Log.d(TAG, "UserRepo is: " + getApp().getComponent().userRepository().toString());
mLoginInteractor = getApp().getComponent().loginInteractor();
Log.d(TAG, "LoginInteractor is: " + mLoginInteractor.getClass().getSimpleName());
}
#Override
public void validateCredentials(String username, String password) {
boolean error = false;
if (!isConnected()) {
noNetworkFailure();
error = true;
}
if (TextUtils.isEmpty(username.trim())) {
// Check that username isn't empty
onUsernameError();
error = true;
}
if (TextUtils.isEmpty(password.trim())) {
// Check that password isn't empty
onPasswordError();
error = true;
}
if (!error) {
getView().showProgress(mAuthenticatingString);
mLoginInteractor.login(username, password, this);
}
}
...
My tests (some of them):
#RunWith(AppRobolectricRunner.class)
#Config(constants = BuildConfig.class)
public class LoginPresenterImplTest {
#Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
private LoginPresenterImpl_ mLoginPresenter;
#Mock
private LoginPresenter.View mLoginViewMock;
#Mock
private LoginInteractor mLoginInteractorMock;
#Captor
private ArgumentCaptor<LoginInteractor.OnLoginFinishedListener> mCaptor;
#Before
public void setUp() {
mLoginPresenter = LoginPresenterImpl_.getInstance_(RuntimeEnvironment.application);
mLoginPresenter.attachView(mLoginViewMock);
mLoginPresenter.mLoginInteractor = mLoginInteractorMock;
}
#After
public void tearDown() throws Exception {
mLoginPresenter.detachView();
mLoginPresenter = null;
}
#Test
public void whenUsernameAndPasswordIsValid_shouldLogin() throws Exception {
String authToken = "Success";
mLoginPresenter.validateCredentials("test", "testtest");
verify(mLoginInteractorMock, times(1)).login(
anyString(),
anyString(),
mCaptor.capture());
mCaptor.getValue().onSuccess(authToken);
verify(mLoginViewMock, times(1)).loginSuccess(authToken);
verify(mLoginViewMock, times(1)).hideProgress();
}
#Test
public void whenUsernameIsEmpty_shouldShowUsernameError() throws Exception {
mLoginPresenter.validateCredentials("", "testtest");
verify(mLoginViewMock, times(1)).setUsernameError();
verify(mLoginViewMock, never()).setPasswordError();
verify(mLoginViewMock, never()).hideProgress();
}
...
As a workaround you can have this:
public class LoginPresenterImpl ... {
...
#VisibleForTesting
public void setLoginPresenter(LoginPresenter.View loginView) {
this.loginView = loginView;
}
}
In test class:
#Before
public void setUp() {
...
MockitoAnnotations.initMocks(this);
loginPresenter.setLoginPresenter(loginView);
}
But, as a rule of thumb, when you see #VisibleForTesting annotation, that means you have ill architecture. Better to refactor your project.
Heads up to Developers that want to use Android Annotations in their project. Watch out when writing unittests that your code doesn't access the Android APIs. The underlying implementation of Android Annotations is heavily dependent on the Android APIs. So the code that is autogenerated could be dependent on this and make it difficult to write unittests.
Always remember that Android Annotations replaces your class with a final class that has an _ added at the end of it's classname. In this generated class a lot of boilerplate code is autogenerated depending on how the original class is annotated. In my case the problem is that I'm working on an Android-project and want a lot of my methods from my presenter to run on the UI-thread. This is achieved using Android Annotations using the #UIThread annotation. But this means that my method is actually wrapped with another method that calls the super-class:
#Override
public void onUsernameError() {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
LoginPresenterImpl_.super.onUsernameError();
return;
}
UiThreadExecutor.runTask("", new Runnable() {
#Override
public void run() {
LoginPresenterImpl_.super.onUsernameError();
}
}
, 0L);
}
My testcase can't get past the line:
...
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
...
And that is of course because we don't have access to the Android APIs in a simple unittest. So in there lies the problem.
Conclusion: You have to be very careful when writing unittests for projects using Android Annotations, that the code that is autogenerated doesn't rely on Android related APIs.
It's the same problem when using androids TextUtil-class.
I am creating Android application using MVP pattern.
For that I am using Retrofit 2 and RxJava. App works fine
But in unit testing I am getting weird error.Same test code sometimes passes, sometimes fails.
Error displays with this message
Wanted but not invoked:
albumView.showProgress();
-> at kz.afckairat.kairat.media.AlbumPresenterTest.checkGetPhotoAlbums(AlbumPresenterTest.java:66)
Actually, there were zero interactions with this mock.
Test class
public class AlbumPresenterTest {
enter code here
private MediaService mediaService;
private AlbumView albumView;
private AlbumPresenterImpl photoAlbumPresenter;
#Before
public void setUp() throws Exception {
albumView = mock(AlbumView.class);
mediaService = mock(MediaService.class);
photoAlbumPresenter = new AlbumPresenterImpl(albumView, mediaService, MediaType.PHOTO);
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
#Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
});
}
#After
public void tearDown() {
RxAndroidPlugins.getInstance().reset();
}
#Test
public void checkGetPhotoAlbums() {
List<Album> albums = getAlbumList();
when(mediaService.getPhotoAlbums()).thenReturn(Observable.just(albums));
photoAlbumPresenter.getAlbums();
verify(albumView).showProgress();
verify(albumView).showAlbums(albums);
verify(albumView).hideProgress();
}
#Test
public void checkGetPhotoAlbumError() {
String msg = "Error";
when(mediaService.getPhotoAlbums()).thenReturn(Observable.error(new IOException(msg)));
photoAlbumPresenter.getAlbums();
verify(albumView).showProgress();
verify(albumView).showError(msg);
verify(albumView).hideProgress();
}
private List<Album> getAlbumList() {
List<Album> albums = new ArrayList<>();
Album album = new Album(1, "Test1", "test1.jpg", "01.01.2016", 2);
albums.add(album);
album = new Album(2, "Test2", "test2.jpg", "01.01.2016", 2);
albums.add(album);
return albums;
}
}
Presenter class which is tested
public class AlbumPresenterImpl implements AlbumPresenter {
private AlbumView view;
private MediaType type;
private List<Album> albums;
private MediaService mediaService;
public AlbumPresenterImpl(AlbumView view, MediaService mediaService, MediaType type) {
this.view = view;
this.mediaService = mediaService;
this.type = type;
}
#Override
public void getAlbums() {
Observable<List<Album>> observable;
if (type.equals(MediaType.VIDEO)) {
observable = mediaService.getVideoAlbums();
} else {
observable = mediaService.getPhotoAlbums();
}
observable.doOnSubscribe(view::showProgress)
.doAfterTerminate(view::hideProgress)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> {
albums = items;
view.showAlbums(albums);
}, throwable -> {
view.showError(throwable.getLocalizedMessage());
});
}
#Override
public void onResume() {
if (albums == null) {
getAlbums();
}
}
#Override
public void onDestroy() {
}
}
Why sometimes test don't pass?
Thanks a lot!
=================================
Update
As #Fred wrote problem was in Schedulers
public class RxSchedulersOverrideRule implements TestRule {
private final RxJavaSchedulersHook mRxJavaSchedulersHook = new RxJavaSchedulersHook() {
#Override
public Scheduler getIOScheduler() {
return Schedulers.immediate();
}
#Override
public Scheduler getNewThreadScheduler() {
return Schedulers.immediate();
}
};
private final RxAndroidSchedulersHook mRxAndroidSchedulersHook = new RxAndroidSchedulersHook() {
#Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
};
// Hack to get around RxJavaPlugins.reset() not being public
// See https://github.com/ReactiveX/RxJava/issues/2297
// Hopefully the method will be public in new releases of RxAndroid and we can remove the hack.
private void callResetViaReflectionIn(RxJavaPlugins rxJavaPlugins)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
Method method = rxJavaPlugins.getClass().getDeclaredMethod("reset");
method.setAccessible(true);
method.invoke(rxJavaPlugins);
}
#Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
RxAndroidPlugins.getInstance().reset();
RxAndroidPlugins.getInstance().registerSchedulersHook(mRxAndroidSchedulersHook);
callResetViaReflectionIn(RxJavaPlugins.getInstance());
RxJavaPlugins.getInstance().registerSchedulersHook(mRxJavaSchedulersHook);
base.evaluate();
RxAndroidPlugins.getInstance().reset();
callResetViaReflectionIn(RxJavaPlugins.getInstance());
}
};
}
}
Code taken from Github a link!
And in Test class
#Rule
public final RxSchedulersOverrideRule mOverrideSchedulersRule = new RxSchedulersOverrideRule();
It seems you override the main thread scheduler with:
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
#Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
});
But from the code, the observables still run on the Schedulers.io() scheduler:
observable.doOnSubscribe(view::showProgress)
.doAfterTerminate(view::hideProgress)
.subscribeOn(Schedulers.io())
// ...
As you may know, the immediate scheduler executes code in the current thread, which I guess since you jump to the io scheduler it's a different one from the one the tests run on.
This will make the test run in one thread and the subscribers/observables in another. This would explain why sometimes the tests pass and sometimes they don't. There's a race condition.
Essential the easiest way is to make sure that at test time you have both observeOn and subscribeOn on Schedulers.immediate() and at run time you have the correct ones, i.e., Schedulers.io() and AndroidSchedulers.mainThread().
You can do this by overriding the schedulers, by passing them as constructors or you could even take a look at this where Dan Lew explains how to use compose to create scheduler transformers. You can then make sure your classes at run time use a proper scheduler transformer and at test time they use some transformer that puts everything on the immediate thread.
I'm developing application based on MVP pattern using retrofit to perform networking. I want to unit test my presenter but it fails.
In my app dataView implements DataView which is mocked by Mockito. In
DataPresenter in onViewCreated method MyApi instance is get from MyApplication and it performs request. Anonymous Subscriber<Data> onNext calls showData(Data data) on dataView. Unfortunatelly Mockito.verify(dataView).showData(data) fails the test. I mocked retrofit client by my self to response in deterministic way.
Code below:
public class DataFragment extends ProgressFragment implements DataView {
protected DataPresenter mDataPresenter;
//[...] initialization arguments boilerplate etc.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mDataPresenter.onViewCreated(mId);
//[...]
}
#Override
public void startLoading() {
setContentShown(false);
}
#Override
public void stopLoading() {
setContentShown(true);
}
#Override
public void showData(Data data) {
setContentEmpty(false);
//[...] present data
}
#Override
public void showError() {
setContentEmpty(true);
setEmptyText(R.string.unknown_error);
}
}
In DataPresenter:
#Override
public void onViewCreated(long id) {
getView().startLoading();
MyApplication.getInstance().getMyApi().checkIn(User.getUser().getFormattedTokenForRequest(),
(int) id).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(new Subscriber<Data>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
getView().showError();
getView().stopLoading();
}
#Override
public void onNext(Data data) {
getView().showData(data);
getView().stopLoading();
}
});
;
}
My test case:
public static final String GOOD_RESPONSE = "[Data in JSON]"
public static final int GOOD_STATUS = 201;
#Mock
DataView mDataView;
#Mock
MyApplication app;
#Mock
SharedPreferencesManager mSharedPreferencesManager;
DataPresenter mDataPresenter;
#Before
public void setUp() throws Exception {
mDataPresenter = new DataPresenterImpl(mDataView);
MyApplication.setInstance(app);
Mockito.when(app.getSharedPreferencesManager()).thenReturn(mSharedPreferencesManager);
Mockito.when(mSharedPreferencesManager.getUser()).thenReturn(null);
}
#Test
public void testCase() throws Exception {
RestAdapter adapter = (new RestAdapter.Builder()).setEndpoint(URL)
.setClient(new MockClient(GOOD_RESPONSE, GOOD_STATUS))
.build();
Mockito.when(app.getMyApi()).thenReturn(adapter.create(MyApi.class));
mCheckInPresenter.onViewCreated(3);
Mockito.verify(checkInView).startLoading();
Mockito.verify(checkInView).showData(new Data());
}
Test fails on "Wanted but not invoked:
dataView.showData(..." .
What is interesting Response execute() is called in MockClient but onNext(Data data) in subscriber included in DataPresenterImpl is not. Any ideas? I guess it is a problem with request being asynchronous.
The problem is that the work is being sent to a different thread and mockito cant verify whats going on. My solution to this would be to create a scheduler factory and mock it out and return the main thread for tests
like these. Something like:
public class schedulerFactory {
public Scheduler io() {
return Schedulers.io();
}
//etc
}
then in your test you would write something like this:
#Mock SchedulerFactory factory
#Before
public void setUp() {
when(factory.io()).thenReturn(Schedulers.mainThread());
}
in general its a good idea to run all the code in the same thread for testing
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.
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.
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();
}
}
};
}
}
I am currently using this implementation. Its easier and works very well for me so far: https://github.com/rosshambrick/RxEspresso