Java – Espresso does not wait for AsyncTask to complete

Espresso does not wait for AsyncTask to complete… here is a solution to the problem.

Espresso does not wait for AsyncTask to complete

According to the Espresso documentation, instrument testing should automatically wait for AsyncTasks to complete. But it doesn’t work. I 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 you return to the UI thread, but it will always succeed.
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 tests are completed even before the background method executes.
How do I get the test to wait for it?

Solution

It turns out that Espresso does wait for AsyncTasks to complete, but only when there is a View interaction.

The reason is that Espresso is in UiController#loopMainThreadUntilIdle() A method that waits for a task to be automatically called behind the scenes at each View interaction. So although my tests don’t need any Views or Activities, I have to create them.

This is what the work test looks like now:

@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()); Because it causes Espresso to wait for the AsyncTask background operation to complete.

TestingActivity is just an empty activity:

public class TestingActivity extends Activity {

}

Related Problems and Solutions