Testing a CursorLoader with Robolectric & Mockito

懵懂的女人 提交于 2019-12-04 04:57:38

I've just gotten Loader tests working in my code. In my case I found it more direct to test the loader itself rather than try to route it through the Fragment code.

I wound up using a slightly modified version of the code from this post: https://groups.google.com/forum/#!msg/robolectric/xY5MF399jA8/V5PnUfh1D-wJ

First, I had to implement some shadow classes because Robolectric2 doesn't include shadow code for the AsyncTaskLoader or Loader classes. If you've never added a shadow class know that it's important you put these in the correct package. Both of these shadows should live in android.support.v4.content.

ShadowLoader

@Implements(Loader.class)
public class ShadowLoader<D> {

// //////////////////////
// Constants

// //////////////////////
// Fields
protected boolean reset;

// //////////////////////
// Constructors

// //////////////////////
// Getter & Setter

// //////////////////////
// Methods from SuperClass/Interfaces

@Implementation
public void reset() {
    reset = true;
}

@Implementation
public boolean isReset() {
    return reset;
}
// //////////////////////
// Methods

// //////////////////////
// Inner and Anonymous Classes
}

ShadowAsyncTaskLoader

@Implements(AsyncTaskLoader.class)
public class ShadowAsyncTaskLoader<D> extends ShadowLoader {

@RealObject
private AsyncTaskLoader<D> asyncTaskLoader;

@Implementation
void executePendingTask() {
    new AsyncTask<Void, Void, D>() {
        @Override
        protected D doInBackground(Void... params) {
            return (D) asyncTaskLoader.loadInBackground();
        }

        @Override
        protected void onPostExecute(D data) {
            updateLastLoadCompleteTimeField();
            asyncTaskLoader.deliverResult(data);
        }

        @Override
        protected void onCancelled(D data) {
            updateLastLoadCompleteTimeField();
            asyncTaskLoader.onCanceled(data);

            executePendingTask();
        }
    }.execute((Void)null);
}


public void setReset(boolean reset) {
    this.reset = reset;
}

private void updateLastLoadCompleteTimeField() {
    try {
        Field mLastLoadCompleteTimeField = AsyncTaskLoader.class.getDeclaredField("mLastLoadCompleteTime");
        if(!mLastLoadCompleteTimeField.isAccessible()) {
            mLastLoadCompleteTimeField.setAccessible(true);
        }
        mLastLoadCompleteTimeField.set(asyncTaskLoader, SystemClock.uptimeMillis());

    } catch(NoSuchFieldException e) {
        e.printStackTrace();
    } catch(IllegalAccessException e) {
        e.printStackTrace();
    }
}
}

Then, depending on your configuration you can add an annotation to use the custom classes

@Config( shadows = { ShadowAsyncTaskLoader.class, ShadowLoader.class})

At this point calling loader.onStartLoading() caused the loader to run as expected without having to hack any wait commands into my test cases.

Hope this helps. I haven't tried using the LoaderManager with this method of testing, so I can't verify that it works through that call.

Note: the reason I added ShadowLoader is because I was finding isReset() was returning true when I didn't expect it to.

The solution is to use:

Robolectric.flushForegroundThreadScheduler();

(Robolectric 3.0)

This will run all tasks immediately, including the CursorLoader.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!