2016-03-10 1 views
7

Ich versuche herauszufinden, wie fehlgeschlagene Tests mit der Verwendung von Espresso erneut ausgeführt werden. Ich denke, es ist ein wenig komplizierter als bei herkömmlichen JUnit-Testfällen, da Sie den Status in Ihrer App vor dem Teststart wiederherstellen müssen.Wie wiederholen Test in Espresso fehlgeschlagen? - Brainstorming

Meine Herangehensweise besteht darin, meine eigene ActivityTestRule zu erstellen, also kopiere ich ganzen Code aus dieser Klasse und nannte sie MyActivityTestRule.

Im Falle von Instrumentierungstests benötigt die Regel auch Informationen darüber, wie wir unsere Aktivität starten möchten. Ich ziehe es vor, es selbst zu starten, anstatt eine Umgebung für mich zu haben. So zum Beispiel:

@Rule 
    public MyActivityTestRule<ActivityToStartWith> activityRule = new MyActivityTestRule<>(
      ActivityToStartWith.class, true, false 
    ); 

So starte ich auch meine Tätigkeit in @Before Anmerkung Methode:

@Before 
    public void setUp() throws Exception { 
     activityRule.launchActivity(new Intent()); 
    } 

Und in @After Anmerkung Methode machen aufzuräumen:

@After 
    public void tearDown() throws Exception { 
     cleanUpDataBaseAfterTest(); 
     returnToStartingActivity(activityRule); 
    } 

Diese Methoden - setUp(), tearDown() müssen unbedingt vor/nach jedem Testlauf aufgerufen werden, um sicherzustellen, dass der App-Zustand während des Testbeginns korrekt ist.


Inside MyActivityTestRule Ich habe bisher nur wenige Änderungen vorgenommen. Das erste, was ist Wechsel gelten Methode aus:

@Override 
    public Statement apply(final Statement base, Description description) { 
     return new ActivityStatement(super.apply(base, description)); 
    } 

Es ist eine unbekannte, aber ist für mich, wie ActivityStatement in ActivityTestRule platziert super.apply Methode hat, damit es auch Test-Anweisung in UiThreadStatement wickelt:

public class UiThreadStatement extends Statement { 
    private final Statement mBase; 
    private final boolean mRunOnUiThread; 

    public UiThreadStatement(Statement base, boolean runOnUiThread) { 
     mBase = base; 
     mRunOnUiThread = runOnUiThread; 
    } 

    @Override 
    public void evaluate() throws Throwable { 
     if (mRunOnUiThread) { 
      final AtomicReference<Throwable> exceptionRef = new AtomicReference<>(); 
      getInstrumentation().runOnMainSync(new Runnable() { 
       public void run() { 
        try { 
         mBase.evaluate(); 
        } catch (Throwable throwable) { 
         exceptionRef.set(throwable); 
        } 
       } 
      }); 
      Throwable throwable = exceptionRef.get(); 
      if (throwable != null) { 
       throw throwable; 
      } 
     } else { 
      mBase.evaluate(); 
     } 
    } 
} 

Nein, was ich mit meinen Tests mache, ich kann niemals Case mRunOnUiThread boolean erstellen, um wahr zu sein. Es wird wahr sein, wenn in meinen Testfällen Tests mit Annotation @UiThreadTest vorhanden sein werden - oder das habe ich aus Code verstanden. Es passiert nie, obwohl ich nicht verwenden so etwas so habe ich beschlossen, diese UiThreadStatement zu ignorieren und ändern MyActivityTestRule zu:

@Override 
    public Statement apply(final Statement base, Description description) { 
     return new ActivityStatement(base); 
    } 

Und meine Testfälle ohne Probleme laufen. Dank dieser alles, was ich verlassen haben -, die um mBase.evaluate Wraps() ist:

private class ActivityStatement extends Statement { 

    private final Statement mBase; 

    public ActivityStatement(Statement base) { 
     mBase = base; 
    } 

    @Override 
    public void evaluate() throws Throwable { 
     try { 
      if (mLaunchActivity) { 
       mActivity = launchActivity(getActivityIntent()); 
      } 
      mBase.evaluate(); 
     } finally { 
      finishActivity(); 
      afterActivityFinished(); 
     } 
    } 
} 

Im Allgemeinen launchActivity wird nur aufgerufen werden, wenn ich wahr im 3. Parameter von ActivityTestRule Konstruktor Wert gesetzt. Aber ich starte Tests selbst in setUp(), so dass es nie passiert.

Von was ich verstanden mBase.evaluate() führt meinen Code innerhalb @Test Annotationsmethode. Es stoppt auch den Testfall während des Werfenwurfs. Das bedeutet, dass ich es fangen kann, und starten Sie es - wie vorgeschlagen: How to Re-run failed JUnit tests immediately?

Und okay, ich habe so etwas wie das:

public class ActivityRetryStatement extends Statement { 

     private final Statement mBase; 
     private final int MAX_RUNS = 2; 

     public ActivityRetryStatement(Statement base) { 
      mBase = base; 
     } 

     @Override 
     public void evaluate() throws Throwable { 

      Throwable throwable = null; 

      for (int i = 0; i < MAX_RUNS; i++) { 

       try { 
        mBase.evaluate(); 
        // if you reach this lane that means evaluate passed 
        // and you don't need to do the next run 
        break; 
       } catch (Throwable t) { 

        // save first throwable if occurred 
        if (throwable == null) { 
         throwable = t; 
        } 

        // my try that didn't work 
        launchActivity(testInitialIntent); 
        // I've returned test to starting screen but 
        // mBase.envaluate() didn't make it run again 

        // it would be cool now to: 
        // - invoke @After 
        // - finish current activity 
        // - invoke @Before again and start activity 
        // - mBase.evaluate() should restart @Test on activity started again by @Before 
       } 
      } 

      finishActivity(); 
      afterActivityFinished(); 

      // if 1st try fail but 2nd passes inform me still that there was error 
      if (throwable != null) { 
       throw throwable; 
      } 
     } 
    } 

Also diese Kommentare in catch-Block sind Teile Ich weiß nicht, wie man machen. Ich habe versucht, launchActivity auf Absicht, die ich in setUp() verwendet, um den Test zum ersten Mal auszuführen. Aber mBase.evaluate() ließ es nicht reagieren (Testfall ging nicht wieder) - nichts passierte + es würde mich dort nicht wirklich retten, denke ich. Mir fehlten einige Initiationen, die ich in @SetUp mache, es wurde nicht erneut aufgerufen.Ich würde wirklich gerne einen Weg finden, wie man den gesamten Testlebenszyklus @Before @Test @ After wieder richtig startet. Vielleicht rufen einige Instrumentation oder TestRunner aus dem Code.

Irgendwelche Gedanken darüber, wie es gemacht werden könnte?

+0

haben Sie irgendwelche Lösungen? –

Antwort

0

Die Antwort ist sehr einfach. Stellen Sie einfach sicher, dass Sie Ihre Espressotests auf Junit 4 aktualisieren, und sehen Sie dann this answer

+0

Ich habe versucht, 'evaluate()' in Schleife zu setzen, aber was ist passiert -> @Test Codeblock wurde erneut ausgeführt, aber das Gerät hat nicht reagiert. Hat es für dich bei ON Espresso funktioniert - keine üblichen Junit-Tests? (Ich benutze Junit Version 4.12) – F1sher

+0

Ja, ich habe dies in unseren Espresso-Tests implementiert und es hat gut funktioniert. Es ist wahrscheinlich die Art, wie Sie Ihre Tests einrichten, das ist das Problem. Können Sie einen Kernpunkt eines Beispieltests posten, der auch die Wiederholungsregel enthält, die in der Antwort implementiert ist, mit der ich verlinkt bin? – gorbysbm

+0

Hey, vielen Dank für diese Information. Momentan bin ich sehr beschäftigt mit Arbeit, aber ich werde es sicher so schnell wie möglich überprüfen, da ich mich sehr dafür interessiere und ich werde dir Feedback geben, wenn es funktioniert oder Code zur Verfügung stellt, damit wir überprüfen können, was ich falsch mache :) – F1sher