Espresso 不等待 AsyncTask 完成
Espresso does not wait for AsyncTask to finish
根据 Espresso 文档,仪器测试应自动等待 AsyncTasks
完成。但它不起作用。我创建了这个简单的测试用例:
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");
}
}
返回 UI 线程时测试应该失败,但它总是成功。
这是测试的 logcat 输出:
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]
如您所见,测试甚至在后台方法执行之前就已完成。
如何让测试等待呢?
它正在按预期工作。由于您在 doInBackground()
中调用 Thread.sleep()
它不会休眠 UI 线程。将该休眠代码移至 onPostExecute
,它应该会按预期工作。
事实证明,Espresso 确实等待 AsyncTasks 完成但仅当存在视图交互时才。
原因是 Espresso 在 UiController#loopMainThreadUntilIdle() 方法期间等待任务,该方法在每次视图交互时在幕后自动调用。因此,尽管我的测试不需要任何视图或活动,但我必须创建它们。
工作测试现在是这样的:
@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");
}
}
最重要的新行是:Espresso.onView(withId(android.R.id.content)).perform(ViewActions.click());
因为它会导致 Espresso 等待 AsyncTask 后台操作完成。
TestingActivity
只是一个空 Activity:
public class TestingActivity extends Activity {
}
您尝试过使用 Espresso.onIdle() 吗?
来自文档:
Loops the main thread until the app goes idle.
Only call this method for tests that do not interact with any UI elements, but require Espresso's main thread synchronisation! This method is mainly useful for test utilities and frameworks that are build on top of Espresso.
根据 Espresso 文档,仪器测试应自动等待 AsyncTasks
完成。但它不起作用。我创建了这个简单的测试用例:
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");
}
}
返回 UI 线程时测试应该失败,但它总是成功。 这是测试的 logcat 输出:
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]
如您所见,测试甚至在后台方法执行之前就已完成。 如何让测试等待呢?
它正在按预期工作。由于您在 doInBackground()
中调用 Thread.sleep()
它不会休眠 UI 线程。将该休眠代码移至 onPostExecute
,它应该会按预期工作。
事实证明,Espresso 确实等待 AsyncTasks 完成但仅当存在视图交互时才。
原因是 Espresso 在 UiController#loopMainThreadUntilIdle() 方法期间等待任务,该方法在每次视图交互时在幕后自动调用。因此,尽管我的测试不需要任何视图或活动,但我必须创建它们。
工作测试现在是这样的:
@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");
}
}
最重要的新行是:Espresso.onView(withId(android.R.id.content)).perform(ViewActions.click());
因为它会导致 Espresso 等待 AsyncTask 后台操作完成。
TestingActivity
只是一个空 Activity:
public class TestingActivity extends Activity {
}
您尝试过使用 Espresso.onIdle() 吗?
来自文档:
Loops the main thread until the app goes idle.
Only call this method for tests that do not interact with any UI elements, but require Espresso's main thread synchronisation! This method is mainly useful for test utilities and frameworks that are build on top of Espresso.