Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Espresso does not wait for AsyncTask to finish

According to the Espresso documentation an instrumentation test should automatically wait for AsyncTasks to finish. But it does not work. I've created this simple test case:

package foo.bar;

import android.os.AsyncTask;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.LargeTest;
import android.support.test.rule.UiThreadTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class ExampleInstrumentedTest {

    private static final String TAG = "ExampleInstrumentedTest";

    @Rule public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();

    @Test
    @UiThreadTest
    public void testAsyncTask() throws Throwable {
        Log.d(TAG, "testAsyncTask entry");
        uiThreadTestRule.runOnUiThread(() -> new AsyncTask<String, Void, Integer>() {
            @Override
            protected Integer doInBackground(String... params) {
                Log.d(TAG, "doInBackground() called with: params = [" + params + "]");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ignored) {
                }
                return params.length;
            }

            @Override
            protected void onPostExecute(Integer integer) {
                Log.d(TAG, "onPostExecute() called with: integer = [" + integer + "]");
                assertEquals(3, (int) integer);
                throw new RuntimeException("this should fail the test");
            }
        }.execute("One", "two", "three"));
        Log.d(TAG, "testAsyncTask end");
    }
}

The test should fail when returning to the UI Thread but it always succeeds. This is the logcat output of the test:

I/TestRunner: started: testAsyncTask(foo.bar.ExampleInstrumentedTest)
D/ExampleInstrumentedTest: testAsyncTask entry
D/ExampleInstrumentedTest: testAsyncTask end
I/TestRunner: finished: testAsyncTask(foo.bar.ExampleInstrumentedTest)
D/ExampleInstrumentedTest: doInBackground() called with: params = [[Ljava.lang.String;@8da3e9]

As you can see the test finishes before the background method is even executed. How can I make the test to wait for it?

like image 316
McFarlane Avatar asked Oct 25 '25 17:10

McFarlane


1 Answers

Turns out that Espresso does wait for AsyncTasks to finish but only if there is a view interaction.

The reason is that Espresso waits for the task during the UiController#loopMainThreadUntilIdle() method which is automatically called behind the curtains on every view interaction. So despite my test does not need any views or activities, i had to create them.

This is how the working test now looks like:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class ExampleInstrumentedTest {

    private static final String TAG = "ExampleInstrumentedTest";

    @Rule public ActivityTestRule<TestingActivity> activityTestRule = new ActivityTestRule<>(
        TestingActivity.class, false, false);

    @Before
    public void setUp() throws Exception {
        activityTestRule.launchActivity(new Intent());
    }

    @Test
    public void testAsyncTask() throws Throwable {
        Log.d(TAG, "testAsyncTask entry");

        AsyncTask<String, Void, Integer> task = new AsyncTask<String, Void, Integer>() {

            @Override
            protected Integer doInBackground(String... params) {
                Log.d(TAG, "doInBackground() called with: params = [" + params + "]");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ignored) {
                }
                return params.length;
            }

            @Override
            protected void onPostExecute(Integer integer) {
                Log.d(TAG, "onPostExecute() called with: integer = [" + integer + "]");
                assertEquals(3, (int) integer);
                throw new RuntimeException("this should fail the test");
            }
        };
        task.execute("One", "two", "three");
        Espresso.onView(withId(android.R.id.content)).perform(ViewActions.click());

        Log.d(TAG, "testAsyncTask end");
    }
}

The most important new line is: Espresso.onView(withId(android.R.id.content)).perform(ViewActions.click()); as it causes Espresso to wait for the AsyncTask background operation to finish.

TestingActivity is just an empty Activity:

public class TestingActivity extends Activity {

}
like image 165
McFarlane Avatar answered Oct 28 '25 07:10

McFarlane



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!