我正在使用 Google 推荐的 Android 测试框架:ActivityInstrumentationTestCase2。我在RANDOM测试运行中遇到了以下错误,但持续死亡。这意味着有时所有测试都通过了(很高兴!),但很多时候它随机失败并出现这三个错误之一。这很令人沮丧,让我对测试结果没有信心。
为了详细描述这些问题,我提供了我的测试类的简化伪代码和以下三个问题。两个测试用例相互独立。
public class FirstActivityTest
extends ActivityInstrumentationTestCase2<FirstActivity> {
private FirstActivity mActivity;
private ActivityMonitor mActivityMonitor;
public FirstActivityTest () {
super(FirstActivity.class);
}
public void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
mActivity = getActivity();
assertNotNull("Cannot start test since target Activity is NULL!", mActivity);
mActivityMonitor = getInstrumentation().addMonitor(SecondActivity.class.getName(), null, false);
}
public void tearDown() throws Exception {
super.tearDown();
if(mActivity != null) {
mActivity.finish();
mActivity = null;
}
if(mActivityMonitor != null) {
getInstrumentation().removeMonitor(mActivityMonitor);
mActivityMonitor = null;
}
}
/**
* Open FirstActivity, enter a text and click submit button.
* Verifies SecondActivity is open.
*/
public void testA_HappyPath() {
Activity secondActivity = null;
try {
//(Omitted) Get edit text and enter a valid value
//(Omitted) Find submitButton view
//Click submit button
TouchUtils.clickView(this, submitButton);
//Wait for result and validate:
secondActivity = mActivityMonitor.waitForActivityWithTimeout(10000);
assertNotNull("Result SecondActivity should NOT be null!", secondActivity );
} finally {
//Clean up:
if(secondActivity != null) {
secondActivity .finish();
secondActivity = null;
}
}
}
/**
* Open FirstActivity, do NOT enter a text and click submit button.
* Verifies error message is returned.
*/
public void testB_SadPath() {
//(Omitted) Find submitButton view
//Click submit button
TouchUtils.clickView(this, submitButton);
//(Omitted) Validate error message is displayed
}
}
现在,我一遍又一遍地运行这两个测试用例(它们将按字母顺序运行),结果如下:
- 两个测试用例都通过了,或者
- testA_HappyPath() 失败,因为 ActivityMonitor.waitForActivityWithTimeout() 返回 NULL SecondActivity。但是当我查看我的设备时,SecondActivity 显示正确。不知何故,测试没有注意到它。为什么?
- 当 testA_HappyPath() 失败时,下一个 testB_SadPath() 将在 setUp() > getActivity() 期间无限期挂起。我想我关闭了 tearDown() 中的所有内容。为什么?
- testB_SadPath() 在 TouchUtils.clickView() 上经常失败,并出现以下错误:“注入另一个应用程序需要INJECT_EVENTS 权限”(无论 testA_HappyPath 是通过还是失败)。为什么?
任何有用的反馈表示赞赏。谢谢!
到目前为止,我已经处理了这 3 个问题,研究了互联网上的许多建议,并进行了几次尝试和错误。然而,没有一个人立即解决了特定问题——通过结合我发现我解决了上面的问题 (1) 和 (2),但仍然有问题 (3) 未解决。以下是我为完成这项工作所做的修复的详细信息。
ISSUE (1) ActivityMonitor.waitForActivityWithTimeout() 返回 NULL
1.1。我现在了解到我必须在 getActivit() 之前声明 getInstrumentation().addMonitor()。请查看我如何更改 setUp() 方法,这以某种方式解决了问题。任何理解为什么这是要求的人请告诉我们,我们很感激。
1.2. 在模拟器上,此调用偶尔会返回 NULL 导致测试失败。我知道这是因为等待时间太短了。因此,增加等待时间有助于防止 ActivityMonitor 过早返回。
ISSUE (2) 下一个 testB_SadPath() 将在 setUp() > getActivity() 期间无限期挂起
2.1。如上所述,这发生在之前的测试 (testA_HappyPath) 失败时。我认为我的 tearDown() 清理了所有内容并准备好运行下一个测试。发生的事情是,testA 正在等待 SecondActivity 出现在屏幕上,但由于 ActivityMonitor.waitForActivityWithTimeout() 返回 NULL,testA 失败。tearDown() 执行得很好。问题是 SecondActivity 确实出现在屏幕上,但它永远不会在 finally 块中关闭,因为它的方法实例“secondActivity”仍然为空。让 SecondActivity 活着并在屏幕上逗留会导致下一个 getActivity() 挂起。我通过更改 finally 块来解决此问题,以确保 SecondActivity 是否存在,它会被关闭。
这些更改总结在下面的代码中(参见 setUp() 和 finally 块)。
public class FirstActivityTest
extends ActivityInstrumentationTestCase2<FirstActivity> {
private FirstActivity mActivity;
private ActivityMonitor mActivityMonitor;
public FirstActivityTest () {
super(FirstActivity.class);
}
public void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
}
public void tearDown() throws Exception {
super.tearDown();
if(mActivity != null) {
mActivity.finish();
mActivity = null;
}
if(mActivityMonitor != null) {
getInstrumentation().removeMonitor(mActivityMonitor);
mActivityMonitor = null;
}
}
/**
* Open FirstActivity, enter a text and click submit button.
* Verifies SecondActivity is open.
*/
public void testA_HappyPath() {
mActivityMonitor = getInstrumentation().addMonitor(SecondActivity.class.getName(), null, false);
mActivity = getActivity();
assertNotNull("Cannot start test since target Activity is NULL!", mActivity);
Activity secondActivity = null;
try {
//(Omitted) Get edit text and enter a valid value
//(Omitted) Find submitButton view
//Click submit button
TouchUtils.clickView(this, submitButton);
//Wait for result and validate:
secondActivity = mActivityMonitor.waitForActivityWithTimeout(20000);
assertNotNull("Result SecondActivity should NOT be null!", secondActivity );
} finally {
//Clean up:
if(secondActivity == null) {
//If empty, wait longer because need to shut down the foreground activity, if any:
secondActivity = mActivityMonitor.waitForActivityWithTimeout(20000);
}
if(secondActivity != null) {
secondActivity .finish();
secondActivity = null;
}
}
}
/**
* Open FirstActivity, do NOT enter a text and click submit button.
* Verifies error message is returned.
*/
public void testB_SadPath() {
mActivity = getActivity();
assertNotNull("Cannot start test since target Activity is NULL!", mActivity);
//(Omitted) Find submitButton view
//Click submit button
TouchUtils.clickView(this, submitButton);
//(Omitted) Validate error message is displayed
}
}
ISSUE (3) testB_SadPath() 在 TouchUtils.clickView() 上经常失败,并出现以下错误:“注入到另一个应用程序需要 INJECT_EVENTS 权限”
我仍然无法解决最后一个问题:-(