2016-02-03 6 views
15

Я пытаюсь написать контрольно-измерительный тест для своего приложения для Android.Android Instrumentation Testing - UI Проблемы с текстом

Я столкнулся с некоторыми странными проблемами с потоками, и я не могу найти решение.

Мой Оригинал Тест:

@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: Метод не может быть вызван на главном потоке приложения (на: магистральный)

(Примечание: Все методы в этой трассе стека не являются моим кодом)

Кажется, чтобы дать мне смешанные результаты ... Если его нужно запустить в исходном потоке, который создал представления, но не может работать в основном потоке, какой поток должен быть запущен?

Я бы очень признателен за любую помощь или предложения!

ответ

13

Эти контрольно-измерительные испытания проходят внутри их собственных приложение. Это также означает, что они запускают свою свою собственную нить.

Вы должны думать о своем приборе как о том, что вы устанавливаете рядом с вашим фактическим приложением, поэтому ваши возможные взаимодействия «ограничены».

Вы должны вызвать все методы просмотра из UIThread/главного потока приложения, поэтому вызов activity.updateDetails(workOrder); из вашей измерительной нити не приложение основной поток. Вот почему исключение выбрасывается.

Вы можете просто запустить код, который нужно проверить на основном потоке, как вы могли бы сделать, если вы называли его в ваше приложение из другого потока с помощью

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

При этом работает тест должен работать ,

Незаконное государственное исключение, которое вы получаете, похоже, связано с вашим взаимодействием с правилом. documentation

Обратите внимание, что при использовании этой аннотации методы инструментария не могут использоваться.

Если вы начинаете/получаете свою деятельность в @Before, она также должна работать.

+1

'runOnUiThread' подход работал в сочетании с' getInstrumentation() waitForIdleSync(); '.. К сожалению, подход '@ Before' не работал. Спасибо за помощь! – Khalos

12

Вы можете запустить часть теста на главном потоке пользовательского интерфейса с помощью UiThreadTestRule.runOnUiThread(Runnable):

@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, вот цитата из Javadoc UiThreadTest «s:

Обратите внимание, в связи с ограничением тока JUnit, методы аннотированных с Before и After также будут выполняться на UI Thread. Рассмотрите возможность использования runOnUiThread (Runnable), если это проблема.

Обратите внимание UiThreadTest (пакет android.support.test.annotation) упомянутый выше отличается от (UiThreadTest (пакет android.test)).

0

В принятом ответе описывается, что происходит отлично.

В качестве дополнения, если кому-то интересно, почему методы эспрессо, которые касаются пользовательского интерфейса, например. perform(ViewActions ...) не нужно делать то же самое, просто потому, что они в конечном итоге делают это позже для нас.

Если следовать perform(ViewActions ...) вы обнаружите, что в конечном итоге делает следующее (в android.support.test.espresso.ViewInteraction):

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

Это mainThreadExecutor сама с аннотацией @MainThread.

Другими словами, Espresso также должен играть по тем же правилам, которые описаны Дэвидом в принятом ответе.

9

Принятый ответ устарел. Правильный способ сделать это сейчас просто:

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