[英]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)
(注意:此堆棧跟蹤中的所有方法都不是我的代碼)
它似乎給了我混合的結果......如果它需要在創建視圖的原始線程上運行但不能在主線程上運行,它應該在哪個線程上運行?
我真的很感激任何幫助或建議!
這些儀器測試在他們自己的應用程序中運行。 這也意味着,它們在自己的線程中運行。
您必須將儀器視為與實際應用程序一起安裝的東西,因此您可能的交互是“有限的”。
您需要從應用程序的 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 限制,使用
Before
和After
注釋的方法也將在 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.