2016-02-03 6 views
15

Ich versuche, einen Instrumentationstest für meine Android-App zu schreiben.Android Instrumentation Testing - Probleme mit der Benutzeroberfläche - Thread

Ich bin in einigen seltsamen Threading-Probleme und ich kann nicht scheinen, eine Lösung zu finden.

My Original Test:

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

Dies führte zu einer

android.view.ViewRootImpl $ CalledFromWrongThreadException: Nur das Original-Thread, das eine Ansicht Hierarchie erstellt wurden, können ihren Standpunkt berühren.

...

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

Das einzige, was die updateDetails() Methode einige setText() Anrufe tut, ist.

Nach ein wenig Recherche schien es, als ob ich eine UiThreadTestRule und android.support.test.annotation.UiThreadTest Annotation hinzufügen würde, um das Problem zu beheben.

@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: Methode kann nicht auf der Hauptanwendungsthread bezeichnet werden (auf: main)

(Anmerkung: Alle die Methoden in diesem Stack-Trace sind nicht mein Code)

Es scheint zu gib mir gemischte Ergebnisse ... Wenn es auf dem ursprünglichen Thread ausgeführt werden muss, der die Ansichten erstellt hat, aber nicht auf dem Hauptthread ausgeführt werden kann, auf welchem ​​Thread sollte es ausgeführt werden?

Ich würde wirklich jede Hilfe oder Vorschläge zu schätzen wissen!

Antwort

13

Diese Instrumentierung Tests laufen innerhalb ihrer eigenen App. Dies bedeutet auch, dass sie in ihrem eigenen Thread laufen.

Sie müssen sich Ihre Instrumentierung als etwas vorstellen, das Sie neben Ihrer eigentlichen App installieren, sodass Ihre möglichen Interaktionen "begrenzt" sind.

Sie müssen alle Ansicht Methoden aus dem UIThread/Hauptthread der Anwendung nennen, so Faden activity.updateDetails(workOrder); von Ihrer Instrumentierung Aufruf ist nicht die Anwendung Hauptthread. Deshalb wird die Ausnahme ausgelöst.

Sie können nur den Code, der Sie auf Ihrem Haupt-Thread testen müssen laufen, wie Sie, wenn Sie es in Ihrem App von einem anderen Thread unter Verwendung

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

Damit läuft, sollte den Test arbeiten fordern tun würden .

Die Ausnahme des ungültigen Status, die Sie erhalten, scheint aufgrund Ihrer Interaktion mit der Regel zu sein. Die documentation Zustände

Beachten Sie, dass Instrumentierungsmethoden möglicherweise nicht verwendet werden, wenn diese Anmerkung vorhanden ist.

Wenn Sie Ihre Aktivität in @Before starten/erhalten, sollte es auch funktionieren.

+1

Der 'runOnUiThread' Ansatz funktioniert, wenn in Kombination mit' getInstrumentation() waitForIdleSync(); '.. Der '@ Before'-Ansatz hat leider nicht funktioniert. Danke für die Hilfe! – Khalos

12

Sie können mit Hilfe von UiThreadTestRule.runOnUiThread(Runnable) Teil Ihres Tests auf dem Haupt-UI-Thread ausgeführt:

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

In den meisten Fällen ist es einfacher, mit Anmerkungen versehen, die Testmethode mit UiThreadTest, kann es jedoch andere Fehler entstehen wie java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main).

EJR, hier ist ein Zitat von UiThreadTest ‚s Javadoc:

Hinweis, aufgrund der aktuellen JUnit Einschränkung, kommentierten Methoden mit Before und After auch auf dem UI-Thread ausgeführt werden. Verwenden Sie runOnUiThread (Runnable), wenn dies ein Problem ist.

Bitte beachten Sie UiThreadTest (Paket android.support.test.annotation) wie oben erwähnt unterscheidet sich von (UiThreadTest (Paket android.test)).

0

Die akzeptierte Antwort beschreibt, was genau vor sich geht.

Als Ergänzung, falls jemand neugierig ist, warum Espresso Methoden, die die UI berühren, z. perform(ViewActions ...) müssen nicht das gleiche tun, es ist einfach weil sie es später für uns tun.

Wenn Sie folgen perform(ViewActions ...) finden Sie es am Ende tun die folgende (in android.support.test.espresso.ViewInteraction):

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

Das mainThreadExecutor selbst mit @MainThread kommentiert.

Mit anderen Worten, Espresso muss auch nach den gleichen Regeln spielen, die von David auf der akzeptierten Antwort beschrieben werden.

9

Die akzeptierte Antwort ist veraltet. Der richtige Weg, dies jetzt zu tun ist einfach:

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