2016-02-03 57 views
15

我想爲我的Android應用程序編寫測試用例測試。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:方法不能在主應用程序線程上調用(上:主)

(注:所有的在這個堆棧跟蹤方法不是我的代碼)

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

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

回答

13

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

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

你需要從應用程序調用的UIThread /主線程全部觀點方法,因此調用從您的儀器線程activity.updateDetails(workOrder);應用程序的主線程。這就是拋出異常的原因。

你可以運行你需要測試你的主線程像你這樣如果你使用

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

有了這個運行測試應該工作稱之爲你的應用程序從不同的線程中執行代碼。

您收到的非法狀態異常似乎是由於您與規則的交互。 documentation狀態

請注意,當存在此註釋時,可能不會使用檢測方法。

如果您在@Before開始/得到您的活動,它也應該工作。

+1

'runOnUiThread'方法與getInstrumentation()。waitForIdleSync();'結合使用。 「@之前」方法不幸運行。謝謝您的幫助! – Khalos

12

您可以用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的限制,與BeforeAfter註解的方法也將在UI線程執行。 考慮使用runOnUiThread(可運行)如果這是一個問題。

請注意上述UiThreadTest(包android.support.test.annotation)是從(UiThreadTest(包android.test))不同。

0

接受的答案描述了正在發生的事情。另外,如果有人很好奇爲什麼Espresso的觸摸界面的方法例如perform(ViewActions ...)不需要做同樣的事情,只是因爲他們最終會爲我們做這件事。

如果按照perform(ViewActions ...)你會發現它最後也做了如下(android.support.test.espresso.ViewInteraction):

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

mainThreadExecutor本身標註了@MainThread

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

9

已接受的答案已棄用。現在正確的做法很簡單:

@Test 
@UiThreadTest 
public void myTest() { 
    // ... 
}