安卓工具测试 - UI 线程问题

2022-09-01 09:20:55

我正在尝试为我的 Android 应用编写检测测试。

我遇到了一些奇怪的线程问题,我似乎找不到解决方案。

我的原始测试:

@RunWith(AndroidJUnit4.class)
public class WorkOrderDetailsTest {

    @Rule
    public ActivityTestRule<WorkOrderDetails> activityRule = new ActivityTestRule<>(WorkOrderDetails.class);

    @Test
    public void loadWorkOrder_displaysCorrectly() throws Exception {
        final WorkOrderDetails activity = activityRule.getActivity();

        WorkOrder workOrder = new WorkOrder();
        activity.updateDetails(workOrder);

        //Verify customer info is displayed
        onView(withId(R.id.customer_name))
                .check(matches(withText("John Smith")));
    }
}

这导致了一个

android.view.ViewRootImpl$CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触及其视图。

...

com.kwtree.kwtree.workorder.WorkOrderDetails.updateDetails(WorkOrderDetails.java:155)

该方法唯一要做的就是一些调用。updateDetails()setText()

经过一些研究,似乎在我的测试中添加一个和注释可以解决这个问题。UiThreadTestRuleandroid.support.test.annotation.UiThreadTest

@UiThreadTest:

@RunWith(AndroidJUnit4.class)
public class WorkOrderDetailsTest {

    //Note: This is new
    @Rule
    public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();

    @Rule
    public ActivityTestRule<WorkOrderDetails> activityRule = new ActivityTestRule<>(WorkOrderDetails.class);

    @Test
    @UiThreadTest //Note: This is new
    public void loadWorkOrder_displaysCorrectly() throws Exception {
        final WorkOrderDetails activity = activityRule.getActivity();

        WorkOrder workOrder = new WorkOrder();
        activity.updateDetails(workOrder);

        //Verify customer info is displayed
        onView(withId(R.id.customer_name))
                .check(matches(withText("John Smith")));
    }
}

java.lang.IllegalStateException:方法不能在主应用程序线程上调用(在:main上)

(注意:此堆栈跟踪中的所有方法都不是我的代码)

它似乎给了我好坏参半的结果...如果它需要在创建视图的原始线程上运行,但不能在主线程上运行,那么它应该在哪个线程上运行?

我真的非常感谢任何帮助或建议!


答案 1

这些检测测试在其自己的应用内运行。这也意味着,它们在自己的线程中运行。

您必须将检测视为与实际应用一起安装的东西,以便可能的交互是“有限的”。

您需要从应用程序的 UIThread /主线程调用所有视图方法,因此从检测线程调用不是应用程序主线程。这就是引发异常的原因。activity.updateDetails(workOrder);

你可以运行需要在主线程上测试的代码,就像你在应用内从其他线程调用它一样,使用

activity.runOnUiThread(new Runnable() {
    public void run() {
        activity.updateDetails(workOrder);
    }
}

运行此命令后,您的测试应该可以正常工作。

您收到的非法状态异常似乎是由于您与规则的交互。文档说明

请注意,当存在此批注时,可能不使用检测方法。

如果你开始/得到你的活动,它也应该工作。@Before


答案 2

你可以在UiThreadTestRule.runOnUiThread(Runnable)的帮助下在主UI线程上运行部分测试:

@Rule
public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();

@Test
public void loadWorkOrder_displaysCorrectly() throws Exception {
    final WorkOrderDetails activity = activityRule.getActivity();

    uiThreadTestRule.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            WorkOrder workOrder = new WorkOrder();
            activity.updateDetails(workOrder);
        }
    });

    //Verify customer info is displayed
    onView(withId(R.id.customer_name))
            .check(matches(withText("John Smith")));
}

在大多数情况下,使用 对测试方法进行注释会更简单,但是,它可能会产生其他错误,例如 。UiThreadTestjava.lang.IllegalStateException: Method cannot be called on the main application thread (on: main)

FYR,这是UiThreadTest的Javadoc的引用:

请注意,由于当前的 JUnit 限制,使用 和 注释的方法也将在 UI 线程上执行。如果这是一个问题,请考虑使用runOnUiThread(Runnable)。BeforeAfter

请注意,上面提到的(软件包)与(UiThreadTest(软件包))不同。UiThreadTestandroid.support.test.annotationandroid.test