Android Instrumentation测试 - UI线程问题

Kha*_*los 24 java testing junit multithreading android

我正在尝试为我的Android应用程序编写一个Instrumentation Test.

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

我的原始测试:

@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")));
    }
}
Run Code Online (Sandbox Code Playgroud)

这导致了一个

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")));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

它似乎给了我混合的结果......如果它需要在创建视图但不能在主线程上运行的原始线程上运行,那么应该运行什么线程?

我真的很感激任何帮助或建议!

Dav*_*jak 23

这些仪器测试在他们自己的应用程序内运行.这也意味着,它们在自己的线程中运行.

您必须将您的仪器视为与实际应用程序一起安装的内容,因此您可能的交互是"有限的".

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

你可以运行你需要在主线程上测试的代码,就像你在不同的线程中通过使用它在应用程序中调用它一样

activity.runOnUiThread(new Runnable() {
    public void run() {
        activity.updateDetails(workOrder);
    }
}
Run Code Online (Sandbox Code Playgroud)

运行此测试应该可以正常运行.

您收到的非法状态异常似乎是因为您与规则的交互.该文档的状态

请注意,当存在此注释时,可能不会使用检测方法.

如果你开始/开展你的活动@Before也应该有效.

  • 当与`getInstrumentation().waitForIdleSync();`结合使用时,`runOnUiThread`方法有效.不幸的是,`@ Before`方法不起作用.谢谢您的帮助! (3认同)

Jer*_*Kao 16

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

@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")));
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,注释测试方法更简单UiThreadTest,但是,它可能会导致其他错误,例如java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main).

FYR,这是来自UiThreadTestJavadoc 的引用:

请注意,由于目前JUnit的限制,带注释的方法Before,并After也将在UI线程执行.如果这是一个问题,请考虑使用runOnUiThread(Runnable).

请注意上面提到的UiThreadTest(包裹android.support.test.annotation)与(UiThreadTest(包裹android.test))不同.


Gre*_*nis 14

已接受的答案现已弃用

实现这一目标的最简单方法就是使用 UiThreadTest

import android.support.test.annotation.UiThreadTest;

        @Test
        @UiThreadTest
        public void myTest() {
            // Set up conditions for test

            // Call the tested method
            activity.doSomethingWithAView()

            // Verify that the results are correct
        }
Run Code Online (Sandbox Code Playgroud)


har*_*njd 7

使用 androidx 测试运行器添加了一个新类,该类UiThreadStatement提供了一种runOnUiThread方法。

        UiThreadStatement.runOnUiThread {
           // call activity here 
        }
Run Code Online (Sandbox Code Playgroud)