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)
(注:このスタックトレースのすべてのメソッドは私のコードではありません)
それは私に混合結果を与えているようです...ビューを作成した元のスレッドで実行する必要があるが、メインスレッドでは実行できない場合、どのスレッドで実行する必要がありますか?
どんな助けや提案も本当に感謝します!
これらのインストルメンテーションテストは、ownアプリ内で実行されます。これは、自分のスレッドで実行されることも意味します。
インストルメンテーションは、実際のアプリと一緒にインストールするものと考える必要があります。そのため、可能な相互作用は「制限」されます。
UIThread /アプリケーションのメインスレッドからすべてのビューメソッドを呼び出す必要があるため、インストルメンテーションスレッドからactivity.updateDetails(workOrder);
を呼び出すことはnotアプリケーションのメインスレッド。これが例外がスローされる理由です。
を使用して別のスレッドからアプリ内で呼び出す場合と同様に、メインスレッドでテストする必要があるコードを実行できます
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)
などの他のエラーが発生する可能性があります。
FYR、 UiThreadTest
のJavadocからの引用です:
現在のJUnitの制限により、
Before
およびAfter
アノテーションが付けられたメソッドもUIスレッドで実行されることに注意してください。 これが問題の場合は、runOnUiThread(Runnable)の使用を検討してください。
上記のUiThreadTest
(パッケージ_Android.support.test.annotation
_)は( UiThreadTest
(パッケージ_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
}
受け入れられた答えは、何が起こっているかを完全に説明しています。
さらに、誰かがUIに触れるエスプレッソのメソッドに興味がある場合など、 perform(ViewActions ...)
は同じことをする必要はありません。それは単に彼らが後で私たちのためにやることになるからです。
perform(ViewActions ...)
に従うと、次のことを行うことになります(Android.support.test.espresso.ViewInteraction
で):
private void runSynchronouslyOnUiThread(Runnable action) {
...
mainThreadExecutor.execute(uiTask);
...
}
mainThreadExecutor
自体に@MainThread
アノテーションが付けられています。
言い換えれば、エスプレッソは、受け入れられた答えについてデビッドが説明したのと同じルールでプレーする必要もあります。