Android Instrumentationtesting – UI线程问题

我正在尝试为我的Android应用程序编写一个Instrumentation Test。

我遇到了一些奇怪的线程问题,我似乎无法find解决scheme。

我的原始testing

@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:只有创build视图层次结构的原始线程可以触及其视图。

com.kwtree.kwtree.workorder.WorkOrderDetails.updateDetails(WorkOrderDetails.java:155)

updateDetails()方法所做的唯一的事情是一些setText()调用。

经过一番研究,好像添加了一个UiThreadTestRuleandroid.support.test.annotation.UiThreadTest注解到我的testing将会解决这个问题。

@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)

(注意:这个堆栈跟踪中的所有方法都不是我的代码)

它似乎给了我不同的结果…如果它需要在创build视图的原始线程上运行,但不能在主线程上运行,应该在哪个线程上运行?

我真的很感激任何帮助或build议!

这些仪器testing在自己应用程序中运行。 这也意味着,他们在自己的线程运行。

您必须将您的仪器视为与实际应用程序一起安装的东西,因此您可能的交互是“有限的”。

你需要从应用程序的UIThread / main线程中调用所有的view方法,所以调用activity.updateDetails(workOrder); 从您的检测线程不是应用程序的主线程。 这就是抛出exception的原因。

你可以运行你需要在主线程上testing的代码,就像你使用不同线程在应用程序中调用它的代码一样

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

有了这个运行你的testing应该工作。

您收到的非法状态exception似乎是由于您与规则的互动。 文档说明

请注意,当存在此注释时,可能不会使用检测方法。

如果你在@Before开始/获得你的活动,它也应该可以工作。

您可以在UiThreadTestRule.runOnUiThread(Runnable)的帮助下,在主UI线程上运行testing的一部分:

 @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注释testing方法会更简单,但是可能会产生其他错误,如java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main)

FYR,这是来自UiThreadTest的Javadoc的报价:

请注意,由于当前的JUnit限制,使用BeforeAfter注释的方法也将在UI Thread上执行。 如果这是一个问题,请考虑使用runOnUiThread(Runnable)。

请注意上面提到的UiThreadTest (包android.support.test.annotation )不同于( UiThreadTest (包android.test ))。

接受的答案已被弃用。 现在正确的做法是:

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

接受的答案描述了正在发生的事情。

此外,如果有人很好奇,为什么Espresso的触摸UI的方法,例如perform(ViewActions ...) ,不需要做同样的事情,这只是因为他们最终会为我们做。

如果你遵循perform(ViewActions ...)你会发现它结束了以下(在android.support.test.espresso.ViewInteraction ):

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

那个mainThreadExecutor本身是用@MainThread注解的。

换句话说,Espresso也需要按照David所说的同样的规则来玩。