簡體   English   中英

Android Instrumentation 測試 - UI 線程問題

[英]Android Instrumentation Testing - UI Thread Issues

我正在嘗試為我的 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:無法在主應用程序線程上調用方法(on:main)

(注意:此堆棧跟蹤中的所有方法都不是我的代碼)

它似乎給了我混合的結果......如果它需要在創建視圖的原始線程上運行但不能在主線程上運行,它應該在哪個線程上運行?

我真的很感激任何幫助或建議!

這些儀器測試在他們自己的應用程序中運行。 這也意味着,它們在自己的線程中運行。

您必須將儀器視為與實際應用程序一起安裝的東西,因此您可能的交互是“有限的”。

您需要從應用程序的 UIThread/主線程調用所有視圖方法,因此調用activity.updateDetails(workOrder); 來自您的檢測線程不是應用程序主線程。 這就是拋出異常的原因。

您可以只運行您需要在主線程上測試的代碼,就像您在應用程序中從不同的線程調用它一樣,使用

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

這樣運行您的測試應該可以工作。

您收到的非法狀態異常似乎是因為您與規則的交互。 文件指出

請注意,存在此注釋時,可能無法使用檢測方法。

如果您在@Before開始/獲取您的活動,它也應該有效。

您可以在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")));
}

在大多數情況下,使用UiThreadTest注釋測試方法更簡單,但是,它可能會導致其他錯誤,例如java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main) UiThreadTest java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main)

FYR,這里引用了UiThreadTest的 Javadoc:

注意,由於當前的 JUnit 限制,使用BeforeAfter注釋的方法也將在 UI 線程上執行。 如果這是一個問題,請考慮使用 runOnUiThread(Runnable)。

請注意上面提到的UiThreadTest (package android.support.test.annotation ) 不同於 ( UiThreadTest (package android.test ))。

已接受的答案現已棄用

最簡單的方法就是使用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
        }

使用 androidx 測試運行器添加了一個新類UiThreadStatement ,它runOnUiThread提供了一個runOnUiThread方法。

        UiThreadStatement.runOnUiThread {
           // call activity here 
        }

接受的答案完美地描述了正在發生的事情。

另外,如果有人好奇為什么 Espresso 的方法接觸 UI,例如perform(ViewActions ...)不需要做同樣的事情,那只是因為他們最終會為我們做這件事。

如果您遵循perform(ViewActions ...)您會發現它最終執行以下操作(在android.support.test.espresso.ViewInteraction ):

private void runSynchronouslyOnUiThread(Runnable action) {
    ...
    mainThreadExecutor.execute(uiTask);
    ...
}

mainThreadExecutor本身用@MainThread注釋。

換句話說,Espresso 也需要按照 David 在接受的答案中描述的相同規則進行操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM