I want to test the functionality of an activity that contains a ListFragment, but I am not sure how to go about this. I have tried a lot, but nothing seems to work.
So the activity I want to test contains a ListFragment, and this ListFragment is populated by using LoaderManager.LoaderCallbacks and a CursorLoader. This CursorLoader queries a ContentProvider, and the onLoadFinished() method swaps the Cursor into the ListAdapter of the ListView.
What I want to achieve with my test is to start the activity and then test if the ListView is populated with the correct data. Because the content of my ContentProvider is based on content retrieved from a webservice with a Service, I thought I should mock the ContentProvider to ensure the test data is what the test expects. But this is easier said than done. I have run into all sorts of problems.
I believe most problems come from the fact that my data is loaded through an AsyncTask by the CursorLoader. I start my loader in the onCreate() method of my ListFragment, but after onCreate() finishes my test executes, because it does not wait for the AsyncTask loading to be finished. And because the loading does not finish before the test executes, my test fails.
This is my test class:
public class TopscorersActivityTest extends ActivityUnitTestCase<TopscorersActivity> {
public static final int TEST_POSITION = 1;
public static final String TEST_NAME = "name";
public static final String TEST_CLUB = "club";
public static final int TEST_GOALS = 2;
private Intent mStartIntent;
private ListView mListView;
private Context mContext;
private ContentResolver mContentResolver;
public TopscorersActivityTest() {
super(TopscorersActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mStartIntent = new Intent();
mContext = new RenamingDelegatingContext(getInstrumentation().getTargetContext(), "test");
mContentResolver = mContext.getContentResolver();
setActivityContext(mContext);
// Setup database fixture
ContentValues values = new ContentValues();
values.put(Topscorers.TOPSCORER_POSITION, TEST_POSITION);
values.put(Topscorers.TOPSCORER_NAME, TEST_NAME);
values.put(Topscorers.TOPSCORER_CLUB, TEST_CLUB);
values.put(Topscorers.TOPSCORER_GOALS, TEST_GOALS);
mContentResolver.delete(Topscorers.CONTENT_URI, null, null);
mContentResolver.insert(Topscorers.CONTENT_URI, values);
}
public void testPreConditions() {
startActivity(mStartIntent, null, null);
assertNotNull(getActivity());
mListView = (ListView) getActivity().findViewById(android.R.id.list);
assertNotNull(mListView);
Cursor cursor = mContentResolver.query(Topscorers.CONTENT_URI, null, null, null, null);
assertEquals(1, cursor.getCount());
}
public void testListPopulatedCorrectly() {
startActivity(mStartIntent, null, null);
getInstrumentation().waitForIdleSync();
ListView listView = (ListView) getActivity().findViewById(android.R.id.list);
assertEquals(1, listView.getCount());
}
}
The testPreConditions() test succeeds, but the testListPopulatedCorrectly() fails because listView.getCount() returns 0.
How can I achieve what I want? Am I even going in the right direction with my test code? Or should I take another approach? If so, what?
Since I have not received any response on my question, I will answer my own question. I decided to go with another approach than used in the code sample of my question. The basic outline of my new approach is:
I switched my base test class to ActivityInstrumentationTestCase2
I created a
MockHttpClientclass, which I inject into my code, and thisMockHttpClientreturns a successfulHttpResponsewith a response entity containing my JSON fixture data. TheMockHttpClientclass implements theHttpClientinterface and returnsnullfor all methods but theexecute()methods that should return aHttpResponseobject.Because the
ListFragmentI am testing registers aBroadcastReceiverto determine that the data retrieval service is finished, I also register aBroadcastReceiverin my test. I block my test with aCountDownLatchuntil the broadcast is received.When the broadcast is received, I use
Thread.sleep(500)to let my activity update theListView. After that I run my assertions against theListView.I annotated my test with
FlakyTest(tolerance=5), which executes the test up to 5 times when assertions fail.I am not sure if this is a good approach, so please feel free to leave some comments. But for now it works. To conclude my answer, the new code for my test:
TEST CLASS
MOCK HTTP CLIENT CLASS