How to check Android activity functionality containing ListFragment?

I want to test the functionality of an activity that contains a ListFragment , but I'm not sure how to do this. I tried a lot, but nothing works.

So the activity I want to test contains a ListFragment , and this ListFragment populated using LoaderManager.LoaderCallbacks and CursorLoader . This CursorLoader requests a ContentProvider , and the onLoadFinished() method replaces Cursor with a ListAdapter ListView .

What I want to achieve with my test is to get started, and then check if the ListView is ListView correct data. Since the content of my ContentProvider based on the content retrieved from the web service using the Service , I thought I should make fun of the ContentProvider to ensure that the test data is what the test expects. But this is easier said than done. I ran into all the problems.

I believe that most of the problems are related to the fact that my data is loaded through AsyncTask using CursorLoade r. I start my loader in the onCreate() method of my ListFragment , but after onCreate() completes, my test runs because it does not wait for AsyncTask load. And since the download did not complete before the test was completed, my test failed.

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 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 use a different approach? If so, then what?

+4
source share
2 answers

Since I did not receive an answer to my question, I will answer my question. I decided to go with a different approach than using my question code example. The basic outline of my new approach:

  • I switched the base test class to ActivityInstrumentationTestCase2

  • I created the MockHttpClient class that I insert into my code, and this MockHttpClient returns a successful HttpResponse with a response object containing my JSON device data. The MockHttpClient class implements the HttpClient interface and returns null for all methods, but the execute() methods, which should return an HttpResponse object.

  • Because the ListFragment I am testing is registering a BroadcastReceiver to determine that the data retrieval service is finished, I am also registering BroadcastReceiver in my test. I block my test with CountDownLatch until a broadcast is received.

  • When the translation is received, I use Thread.sleep(500) so that my activity updates the ListView . After that, I launch my claims against ListView .

  • I annotated my test using FlakyTest(tolerance=5) , which runs the test up to 5 times when statements fail.

I'm not sure this is a good approach, so please feel free to leave comments. But for now, it works. To complete my answer, new code for my test:

TEST CLASS

 public class TopscorersActivityTest extends ActivityInstrumentationTestCase2<TopscorersActivity> { public static final String JSON = "[" + "{\"position\": 1, \"name\": \"Bas Dost\", \"club\": \"sc Heerenveen\", \"goals\": \"16\" }," + "{\"position\": 2, \"name\": \"Dries Mertens\", \"club\": \"PSV\", \"goals\": \"13\"}," + "{\"position\": 3, \"name\": \"Luuk de Jong\", \"club\": \"FC Twente\", \"goals\": \"12\"}" + "]"; private TopscorersActivity mActivity; private ListView mListView; public TopscorersActivityTest() { super("com.example.package", TopscorersActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); ConnectivityUtils.setHttpClient(MockHttpClient.createInstance(JSON)); mActivity = getActivity(); mListView = (ListView) getActivity().findViewById(android.R.id.list); } @Override protected void tearDown() throws Exception { super.tearDown(); ConnectivityUtils.setHttpClient(null); } @MediumTest public void testPreconditions() { assertNotNull(mActivity); assertNotNull(mListView); assertEquals(0, mListView.getFirstVisiblePosition()); } @FlakyTest(tolerance=5) @LargeTest public void testListItemsPopulatedCorrectly() throws InterruptedException { waitForBroadcast(mActivity, TopscorersService.BROADCAST_ACTION, Intent.CATEGORY_DEFAULT); assertEquals(3, mListView.getCount()); // First list item View view = mListView.getChildAt(0); assertNotNull(view); TextView positionTextView = (TextView) view.findViewById(R.id.topscorerPositionTextView); TextView nameTextView = (TextView) view.findViewById(R.id.topscorerNameTextView); TextView goalsTextView = (TextView) view.findViewById(R.id.topscorerGoalsTextView); assertEquals("1", positionTextView.getText()); assertEquals("16", goalsTextView.getText()); assertEquals( Html.fromHtml("Bas Dost<br /><i>sc Heerenveen</i>").toString(), nameTextView.getText().toString() ); // Second list item view = mListView.getChildAt(1); assertNotNull(view); positionTextView = (TextView) view.findViewById(R.id.topscorerPositionTextView); nameTextView = (TextView) view.findViewById(R.id.topscorerNameTextView); goalsTextView = (TextView) view.findViewById(R.id.topscorerGoalsTextView); assertEquals("2", positionTextView.getText()); assertEquals("13", goalsTextView.getText()); assertEquals( Html.fromHtml("Dries Mertens<br /><i>PSV</i>").toString(), nameTextView.getText().toString() ); // Third list item view = mListView.getChildAt(2); assertNotNull(view); positionTextView = (TextView) view.findViewById(R.id.topscorerPositionTextView); nameTextView = (TextView) view.findViewById(R.id.topscorerNameTextView); goalsTextView = (TextView) view.findViewById(R.id.topscorerGoalsTextView); assertEquals("3", positionTextView.getText()); assertEquals("12", goalsTextView.getText()); assertEquals( Html.fromHtml("Luuk de Jong<br /><i>FC Twente</i>").toString(), nameTextView.getText().toString() ); } private void waitForBroadcast(Context context, String action, String category) throws InterruptedException { final CountDownLatch signal = new CountDownLatch(1); IntentFilter intentFilter = new IntentFilter(action); intentFilter.addCategory(category); BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { signal.countDown(); } }; context.registerReceiver(broadcastReceiver, intentFilter); signal.await(1500, TimeUnit.MILLISECONDS); context.unregisterReceiver(broadcastReceiver); Thread.sleep(500); } } 

CLIENT CLASS MOCK

 public class MockHttpClient implements HttpClient { private HttpResponse mHttpResponse; /** * A MockHttpClient with an HTTP 1.1 200 OK response * * @param response * @return * @throws UnsupportedEncodingException */ public static HttpClient createInstance(String response) throws UnsupportedEncodingException { return createInstance(200, "OK", response); } /** * A MockHttpClient with an HTTP 1.1 response * * @param statusCode * @param reasonPhrase * @param response * @return * @throws UnsupportedEncodingException */ public static HttpClient createInstance(int statusCode, String reasonPhrase, String response) throws UnsupportedEncodingException { return createInstance(HttpVersion.HTTP_1_1, statusCode, reasonPhrase, response); } /** * * @param version * @param statusCode * @param reasonPhrase * @param response * @return * @throws UnsupportedEncodingException */ public static HttpClient createInstance(ProtocolVersion version, int statusCode, String reasonPhrase, String response) throws UnsupportedEncodingException { StatusLine statusLine = new BasicStatusLine(version, statusCode, reasonPhrase); HttpResponse httpResponse = new BasicHttpResponse(statusLine); HttpEntity httpEntity = new StringEntity(response); httpResponse.setEntity(httpEntity); return new MockHttpClient(httpResponse); } /** * Constructor. * * @param httpResponse */ private MockHttpClient(HttpResponse httpResponse) { mHttpResponse = httpResponse; } /** * * @param request * @return */ public HttpResponse execute(HttpUriRequest request) { return mHttpResponse; } @Override public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException, ClientProtocolException { return mHttpResponse; } @Override public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException, ClientProtocolException { return mHttpResponse; } @Override public <T> T execute(HttpUriRequest arg0, ResponseHandler<? extends T> arg1) throws IOException, ClientProtocolException { return null; } @Override public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException, ClientProtocolException { return mHttpResponse; } @Override public <T> T execute(HttpUriRequest arg0, ResponseHandler<? extends T> arg1, HttpContext arg2) throws IOException, ClientProtocolException { return null; } @Override public <T> T execute(HttpHost arg0, HttpRequest arg1, ResponseHandler<? extends T> arg2) throws IOException, ClientProtocolException { return null; } @Override public <T> T execute(HttpHost arg0, HttpRequest arg1, ResponseHandler<? extends T> arg2, HttpContext arg3) throws IOException, ClientProtocolException { return null; } @Override public ClientConnectionManager getConnectionManager() { return null; } @Override public HttpParams getParams() { return null; } } 
+2
source

Instead of using the Thread.wait(500) call mentioned in @ Jan-Henk, in the unit test, I used DataSetObserver to do:

 mListView.getAdapter().registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { assertTrue(mListView.getCount() > 0); } }); 

Doing this allows you to get the changes exactly when it happens.

+4
source

Source: https://habr.com/ru/post/1392906/


All Articles