gpt4 book ai didi

Android单元测试: ActivityMonitor waitForActivityWithTimeout return NULL, getActivity永不返回,INJECT_EVENTS权限错误

转载 作者:太空宇宙 更新时间:2023-11-03 10:48:02 24 4
gpt4 key购买 nike

我正在使用 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
}
}

现在,我一遍又一遍地运行这两个测试用例(它们将按字母顺序运行),结果如下:

  1. 两个测试用例都通过了,或者
  2. testA_HappyPath() 失败,因为 ActivityMonitor.waitForActivityWithTimeout() 返回 NULL SecondActivity。但是当我查看我的设备时,SecondActivity 显示正确。不知何故,测试没有注意到它。为什么?
  3. 当 testA_HappyPath() 失败时,下一个 testB_SadPath() 将在 setUp() > getActivity() 期间无限期挂起。我想我关闭了 tearDown() 中的所有内容。为什么?
  4. testB_SadPath() 在 TouchUtils.clickView() 上经常失败,并出现以下错误:“注入(inject)另一个应用程序需要 INJECT_EVENTS 许可”(无论 testA_HappyPath 是否通过)。为什么?

感谢任何有帮助的反馈。谢谢!


到目前为止,我已经处理了这 3 个问题,研究了互联网上的许多建议,并进行了几次尝试和错误。然而,没有一个人立即解决了特定问题 - 通过结合我发现的问题,我解决了上面的问题 (1) 和 (2),但仍有问题 (3) 未解决。以下是我为完成这项工作所做的修复的详细信息。

问题 (1) ActivityMonitor.waitForActivityWithTimeout() 返回 NULL

1.1。我现在了解到我必须在 getActivit() 之前声明 getInstrumentation().addMonitor()。请查看我如何更改 setUp() 方法,这以某种方式解决了问题。任何理解为什么这是要求的人请告诉我们,我们很感激。

1.2。在模拟器上,此调用偶尔会返回 NULL,导致测试失败。我了解到这是因为等待时间太短了。因此,增加等待时间有助于防止 ActivityMonitor 过早返回。

问题 (2) 下一个 testB_SadPath() 将在 setUp() > getActivity() 期间无限期挂起

2.1。如上所述,这发生在先前的测试 (testA_HappyPath) 失败时。我以为我的 tearDown() 清理了所有东西并准备好运行下一个测试。发生的事情是,testA 正在等待 SecondActivity 出现在屏幕上,但由于 ActivityMonitor.waitForActivityWithTimeout() 返回 NULL,testA 失败了。 tearDown() 执行得很好。问题是 SecondActivity 确实出现在屏幕上,但它永远不会在 finally block 中关闭,因为它的方法实例“secondActivity”仍然为空。让 SecondActivity 处于 Activity 状态并在屏幕上徘徊导致下一个 getActivity() 挂起。我通过更改 finally block 来修复此问题,以确保如果 SecondActivity 曾经存在,它就会关闭。

这些更改总结在下面的代码中(请参阅 setUp() 和 finally block )。

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
}
}

问题 (3) testB_SadPath() 通常在 TouchUtils.clickView() 上失败并出现以下错误:“注入(inject)到另一个应用程序需要 INJECT_EVENTS 权限”

我仍然无法解决最后一个问题:-(

最佳答案

问题 (3) testB_SadPath() 通常在 TouchUtils.clickView() 上失败并出现以下错误:“注入(inject)到另一个应用程序需要 INJECT_EVENTS 权限”

我在我的 Android 单元测试中找到了避免此问题的替代方法。我没有使用 TouchUtils.clickView(),而是通过调用 performClick() 直接对按钮本身执行单击操作。下面修改测试代码解决了我偶尔出现的 INJECT_EVENTS 权限错误。特别是,请参阅 populateDataAndClickSubmit()。

/**
* 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 {
String dataValue = "MyNameIsNoLongerFooNorBar";
populateDataAndClickSubmit(dataValue);

//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);

String dataValue = null;
populateDataAndClickSubmit(dataValue);

//(Omitted) Validate error message is displayed
}

private void populateDataAndClickSubmit(final String dataValueString) {
final EditText editDataView = //(omitted) find it from the activity layout
final Button submitButton = //(Omitted) Find submitButton view

mActivity.runOnUiThread(
new Runnable() {
public void run() {
editDataView.setText(dataValueString);
submitButton.performClick();
}
}
);

//Wait and allow app to be idle while performClick to finish and activity re-drawn:
getInstrumentation().waitForIdleSync();
}

注意事项:

  1. 此解决方案不是 touchUtils.clickView() 偶尔抛出注入(inject)事件权限错误原因的答案
  2. View.performClick() 要求您的 Activity 让相关 View 实现 OnClickListener()。在我的例子中,SubmitButton 已经有了,所以这是一个方便的测试代码更改。
  3. getInstrumentation().waitForIdleSync() 允许测试代码空闲,直到应用完成工作并适本地重新绘制其布局。如果您查看它的 java 代码,则同一行在 touchUtils.clickView() 中执行。

关于Android单元测试: ActivityMonitor waitForActivityWithTimeout return NULL, getActivity永不返回,INJECT_EVENTS权限错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19014708/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com