web-dev-qa-db-ja.com

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()呼び出しだけです。

少し調べてみると、UiThreadTestRuleと_Android.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)

(注:このスタックトレースのすべてのメソッドは私のコードではありません)

それは私に混合結果を与えているようです...ビューを作成した元のスレッドで実行する必要があるが、メインスレッドでは実行できない場合、どのスレッドで実行する必要がありますか?

どんな助けや提案も本当に感謝します!

24
Khalos

これらのインストルメンテーションテストは、ownアプリ内で実行されます。これは、自分のスレッドで実行されることも意味します。

インストルメンテーションは、実際のアプリと一緒にインストールするものと考える必要があります。そのため、可能な相互作用は「制限」されます。

UIThread /アプリケーションのメインスレッドからすべてのビューメソッドを呼び出す必要があるため、インストルメンテーションスレッドからactivity.updateDetails(workOrder);を呼び出すことはnotアプリケーションのメインスレッド。これが例外がスローされる理由です。

を使用して別のスレッドからアプリ内で呼び出す場合と同様に、メインスレッドでテストする必要があるコードを実行できます

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

これを実行すると、テストが機能するはずです。

受け取っている違法な状態の例外は、ルールとの相互作用によるものと思われます。 ドキュメント 状態

この注釈が存在する場合、インストルメンテーションメソッドは使用できないことに注意してください。

@Beforeでアクティビティを開始/取得した場合も動作するはずです。

23
David Medenjak

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)などの他のエラーが発生する可能性があります。

FYR、 UiThreadTest のJavadocからの引用です:

現在のJUnitの制限により、BeforeおよびAfterアノテーションが付けられたメソッドもUIスレッドで実行されることに注意してください。 これが問題の場合は、runOnUiThread(Runnable)の使用を検討してください。

上記のUiThreadTest(パッケージ_Android.support.test.annotation_)は( UiThreadTest (パッケージ_Android.test_))とは異なることに注意してください。

16
Jeremy Kao

受け入れられた答えは廃止予定

これを達成する最も簡単な方法は、単に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
        }
14
Greg Ennis

受け入れられた答えは、何が起こっているかを完全に説明しています。

さらに、誰かがUIに触れるエスプレッソのメソッドに興味がある場合など、 perform(ViewActions ...)は同じことをする必要はありません。それは単に彼らが後で私たちのためにやることになるからです。

perform(ViewActions ...)に従うと、次のことを行うことになります(Android.support.test.espresso.ViewInteractionで):

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

mainThreadExecutor自体に@MainThreadアノテーションが付けられています。

言い換えれば、エスプレッソは、受け入れられた答えについてデビッドが説明したのと同じルールでプレーする必要もあります。

1
joakim