2016-02-03 23 views
15

Sto provando a scrivere un test di strumentazione per la mia app per Android.Test di strumentazione Android - Problemi di filettatura dell'interfaccia utente

Sono in esecuzione in alcuni problemi di threading strano e non riesco a trovare una soluzione.

La mia prova originale:

@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"))); 
    } 
} 

Ciò ha comportato un

android.view.ViewRootImpl $ CalledFromWrongThreadException: Solo il thread originale che ha creato una gerarchia vista può toccare i suoi panorami.

...

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

L'unica cosa che il metodo updateDetails() non fa altro che alcuni setText() chiamate.

Dopo aver cercato un po ', è sembrato come aggiungere un'annotazione UiThreadTestRule e android.support.test.annotation.UiThreadTest al mio test per risolvere il problema.

@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: metodo non può essere chiamato sul thread principale dell'applicazione (a: principale)

(Nota: Tutti i metodi in questa traccia dello stack non sono il mio codice)

Sembra mi sta dando risultati misti ... Se ha bisogno di essere eseguito sul thread originale che ha creato le viste ma non può essere eseguito sul thread principale, su quale thread dovrebbe essere eseguito?

Apprezzerei davvero qualsiasi aiuto o suggerimento!

risposta

13

Questi test di strumentazione sono eseguiti all'interno dell'app propria. Ciò significa anche che eseguono nella propria filettatura .

È necessario pensare alla propria strumentazione come qualcosa che si installa accanto alla propria app, quindi le possibili interazioni sono "limitate".

è necessario chiamare tutti i metodi vista dalla/thread principale dell'applicazione UIThread, quindi chiamando activity.updateDetails(workOrder); dal tuo thread strumentazione è non il thread principale dell'applicazione. Questo è il motivo per cui viene lanciata l'eccezione.

Si può solo eseguire il codice è necessario testare sul tuo thread principale come si farebbe se si stesse chiamando dentro la vostra applicazione da un thread diverso utilizzando

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

Con questa esecuzione il test dovrebbe funzionare .

L'eccezione di stato illegale che si riceve sembra essere dovuta all'interazione con la regola. Gli stati documentation

Si noti che i metodi di strumentazione non possono essere utilizzati quando questa annotazione è presente.

Se si avvia/ottiene la propria attività in @Before, dovrebbe anche funzionare.

+1

L'approccio 'runOnUiThread' lavorato quando combinato con' getInstrumentation() waitForIdleSync(); '.. L'approccio '@ Before' non ha funzionato purtroppo. Grazie per l'aiuto! – Khalos

12

È possibile eseguire parte del test sul thread principale dell'interfaccia utente con l'aiuto di 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"))); 
} 

Nella maggior parte dei casi è più semplice di annotare il metodo di prova con UiThreadTest, tuttavia, può incorrere in altri errori come ad esempio java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main).

FYR, qui è una citazione da Javadoc UiThreadTest s':

nota, a causa della limitazione di corrente JUnit, metodi annotati con Before e After saranno eseguiti sul thread dell'interfaccia utente. Considerare l'utilizzo di runOnUiThread (Runnable) se questo è un problema.

Si prega di notare UiThreadTest (pacchetto android.support.test.annotation) di cui sopra è diverso da (UiThreadTest (pacchetto android.test)).

0

La risposta accettata descrive ciò che sta accadendo perfettamente.

Come aggiunta, nel caso qualcuno sia curioso del perché i metodi di Espresso che toccano l'interfaccia utente, ad es. perform(ViewActions ...) non è necessario fare lo stesso, è semplicemente perché finiscono per farlo più tardi per noi.

Se seguite perform(ViewActions ...) lo troverete finisce per fare quanto segue (in android.support.test.espresso.ViewInteraction):

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

Che mainThreadExecutor è a sua volta annotato con @MainThread.

In altre parole, Espresso ha anche bisogno di giocare con le stesse regole descritte da David sulla risposta accettata.

9

La risposta accettata è deprecata. Il modo corretto per farlo ora è semplicemente:

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