更新#1:更多信息添加到这篇文章的末尾
我是 Android 开发和测试的新手。
我有三个浓缩咖啡测试。第一个测试通过,但第二个测试不会运行,因为在第二个测试之前调用 setUp() 方法中的 getActivity() 总是失败:
Blockquote java.lang.RuntimeException:无法在 45 秒内启动 Intent Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=my.packagename/.ActivityMain }。...
第三次测试通过。
我在创建时没有任何长时间运行的操作、动画或网络调用。我可以单击应用程序中的所有菜单项,手动重复测试流程而不会出现问题。
由于某种原因,第一个测试在第二个测试之前“中断”了 setUp() 中的下一个 getActivity() 调用。以下所有测试(在第二次测试之后)将运行正常。
我发现了类似的问题,但看起来它没有解决,而且问题有点不同。
测试代码:
import static com.google.android.apps.common.testing.ui.espresso.Espresso.onData;
import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
import static com.google.android.apps.common.testing.ui.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isDisplayed;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import net.humblegames.bodylasticscalculator.ActivityMain;
import net.humblegames.bodylasticscalculator.R;
import net.humblegames.bodylasticscalculator.applogic.BandSystem;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import android.app.Activity;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
public class MenuNavigationTestExperiment extends
ActivityInstrumentationTestCase2<ActivityMain> {
private String TAG = getClass().getSimpleName();
private Activity activity;
public MenuNavigationTestExperiment() {
super(ActivityMain.class);
}
@Before
public void setUp() throws Exception {
Log.d(TAG, "SETUP");
activity = getActivity();
super.setUp();
}
@After
public void tearDown() throws Exception {
Log.d(TAG, "TEARDOWN");
super.tearDown();
}
public void testFirst() {
Log.d(TAG, "testFirst");
clickMenuItem("Select band system");
onData(allOf(is(instanceOf(BandSystem.class)), hasName("MMA Training")))
.onChildView(withId(R.id.label)).perform(click());
clickMenuItem("About");
onView(withId(R.id.about_webview)).check(matches(isDisplayed()));
}
public void testSecond() {
Log.d(TAG, "testSecond");
// this test will not run
}
public void testThird() {
Log.d(TAG, "testThird");
// this test will pass
}
// ------------------ HELPER METHODS ---------------------------------------
private void clickMenuItem(String menuItem) {
Log.d(TAG, "clickMenuItem");
openActionBarOverflowOrOptionsMenu(getInstrumentation()
.getTargetContext());
onView(withText(menuItem)).perform(click());
}
private Matcher<BandSystem> hasName(final String name) {
Log.d(TAG, "hasName");
return new BaseMatcher<BandSystem>() {
@Override
public boolean matches(final Object item) {
final BandSystem foo = (BandSystem) item;
return name == foo.getName();
}
@Override
public void describeTo(final Description description) {
description.appendText("getName should return ").appendValue(
name);
}
};
}
}
错误跟踪:
java.lang.RuntimeException: Could not launch intent Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=net.humblegames.bodylasticscalculator/.ActivityMain } within 45 seconds. Perhaps the main thread has not gone idle within a reasonable amount of time? There could be an animation or something constantly repainting the screen. Or the activity is doing network calls on creation? See the threaddump logs. For your reference the last time the event queue was idle before your activity launch request was 1395582828351 and and now the last time the queue went idle was: 1395582830169. If these numbers are the same your activity might be hogging the event queue.
at com.google.android.apps.common.testing.testrunner.GoogleInstrumentation.startActivitySync(GoogleInstrumentation.java:277)
at android.test.InstrumentationTestCase.launchActivityWithIntent(InstrumentationTestCase.java:119)
at android.test.InstrumentationTestCase.launchActivity(InstrumentationTestCase.java:97)
at android.test.ActivityInstrumentationTestCase2.getActivity(ActivityInstrumentationTestCase2.java:104)
at net.humblegames.bodylasticscalculator.test.MenuNavigationTestExperiment.setUp(MenuNavigationTestExperiment.java:42)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner.onStart(GoogleInstrumentationTestRunner.java:167)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
更新#1
我为该项目创建了一个干净的 eclipse 项目和一个干净的 Espresso 测试。我能够在 Espresso 测试运行期间重现相同的错误(但我仍然不确定此错误的原因在我的真实应用中是否相同):
目标应用程序有 3 个活动(主要、第二个、第三个)。用户通过单击菜单项在它们之间导航:主要活动开始第二个,第二个活动开始第三个。
我发现如果我在第一次测试中调用 clickMenuItem()(见下文)和 Thread.sleep(500),这会导致在第二次测试之前 getActivity() 调用中的 setUp() 崩溃。如果睡眠超时小于 0.5 秒,则不会发生崩溃。
同时如果调用 Thread.sleep(10000) 并且不调用 clickMenuItem() 也没有崩溃。它将在第一次测试中成功休眠 10 秒,并且也将通过第二次测试。
测试代码:
public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity> {
private String TAG = getClass().getSimpleName();
private Activity activity;
public MainActivityTest() {
super(MainActivity.class); }
public void setUp() throws Exception {
Log.d(TAG, "SETUP");
activity = getActivity();
super.setUp(); }
public void tearDown() throws Exception {
Log.d(TAG, "TEARDOWN");
super.tearDown(); }
public void testFirst() throws InterruptedException {
Log.d(TAG, "testFirst");
clickMenuItem("Start second activity");
clickMenuItem("Start third activity");
Thread.sleep(500);
}
public void testSecond() {
Log.d(TAG, "testSecond");
// this test will not run }
private void clickMenuItem(String menuItem) {
Log.d(TAG, "clickMenuItem");
openActionBarOverflowOrOptionsMenu(getInstrumentation()
.getTargetContext());
onView(withText(menuItem)).perform(click()); } }
目标代码:
主要活动:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true; }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.action_start_second_activity:
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
} } }
第二个活动:
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second); }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.second, menu);
return true; }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.action_start_third_activity:
Intent intent = new Intent(this, ThirdActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
} } }
第三项活动:
public class ThirdActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third); }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.third, menu);
return true; }
}