2017-05-10 4 views
9

Ich versuche RecyclerView mit AndroidJunit4 zu testen, ist es mein Testcode:Android RecyclerView Adapter Artikelanzahl wird 0 auf Unit-Tests Rückkehr

@Rule 
    public ActivityTestRule<ProductListActivity> rule = new ActivityTestRule<>(ProductListActivity.class); 

............................ 
.......................... 

@Test 
    public void ensureDataIsLoadingOnSuccess() throws Exception { 
     ProductListActivity activity = rule.getActivity(); 
     ........................... 
     ............ 

     activity.runOnUiThread(new Runnable() { 
     public void run() { 
      activity.displayProducts(asList(product1, product2), 0); 
     } 
    }); 

     assertEquals(2, mAdapter.getItemCount()); 
     assertThat(((ProductAdapter) mAdapter).getItemAtPosition(0),sameInstance(product1)); 
     assertThat(((ProductAdapter) mAdapter).getItemAtPosition(1),sameInstance(product2)); 


    } 

Hier ist mein Code für displayProducts() in Aktivität:

@Override 
    public void displayProducts(List<Product> products, Integer pageNo) { 
     progressBar.setVisibility(View.GONE); 
     if (pageNo == 0 && products.size() == 0) { 
      noProductTextView.setVisibility(View.VISIBLE); 
     } else { 
      mProductAdapter.addProduct(products); 
      noProductTextView.setVisibility(View.GONE); 
      productListView.setVisibility(View.VISIBLE); 
     } 
    } 

Es gibt Fehler wie:

junit.framework.AssertionFailedError: expected:<2> but was:<0> 
at junit.framework.Assert.fail(Assert.java:50) 
at junit.framework.Assert.failNotEquals(Assert.java:287) 
at junit.framework.Assert.assertEquals(Assert.java:67) 
at junit.framework.Assert.assertEquals(Assert.java:199) 
at junit.framework.Assert.assertEquals(Assert.java:205) 
at com.kaushik.myredmart.ui.ProductListActivityTest.ensureDataIsLoadingOnSuccess(ProductListActivityTest.java:94) 

Bitte helfen Sie, was das Problem in meinem Code ist?

+0

Wie richte ich 'mAdapter' ein? – tynn

+1

Animationen sind dein Feind beim Testen, jeder Fortschrittsbalken, benutzerdefinierte Animationen oder jede Arbeit, die an einem nicht-asynchronen Thread-Pool ausgeführt wird, werden nicht bei Espresso registriert und es läuft einfach durch die anderen Behauptungen, weil es denkt, dass es nichts zu warten gibt. Versuchen Sie auch, Ihre Tests zu schreiben, die Sie injizieren und setzen Sie die Daten in Setup und Teardown, dann wird Ihre Aktivität während des Aufbaus haben. Haben Sie notifydataset nach displayProducts() geändert? – originx

+0

Woher kommt mAdapter? Ich denke, Sie überprüfen den falschen Adapter – Tudor

Antwort

-1

Es gibt ein Problem, das ich hier sehen kann, fragen Sie die Liste Größe, bevor der Main/UI-Thread es aktualisieren kann. Sie müssen also im aktuellen Thread warten, bis die Aktivität die Aktualisierung der Liste im Hauptthread abgeschlossen hat.

Sie tun können,

Thread.sleep(500); 

im Testklasse zu warten, um die Liste Setzungsverhalten in Aktivität zu testen, und Sie erhalten die Behauptung finden ihre Gültigkeit.

Da der Hauptthread unendlich lange läuft, bis die Anwendung ausgeführt wird, müssen Sie eine Callback-Schnittstelle implementieren, die von der Aktivität bereitgestellt wird, um darüber informiert zu werden, wann das Auffüllen der Liste abgeschlossen ist.

+0

Die Frage ist, warum Espresso nicht wartet, bis 'MessageQueue' leer ist? – azizbekian

+0

@azizbekian, weil es auf einem anderen Thread (Hintergrund) läuft. Fügen Sie ein Beobachtungsdesignmuster hinzu und fangen Sie es genau mit der Uhrzeit ab, an der es verwendet wird. Verwenden Sie andernfalls die Thread-Schlafmethode. –

+0

Ihre Klarstellung ist nicht wahr: Espresso läuft auf einem Hintergrund-Thread, ** aber ** es hört auf die 'MessageQueue' des UI-Threads leer zu sein, und sobald es leer ist, erst nachdem Espresso mit Matcher fortfahren wird. Sie schlagen eine Problemumgehung mit Ihrer Antwort vor, erläutern jedoch nicht, warum das Problem auftritt. – azizbekian

0

Der Grund ist, dass Ihr Espresso-Test nicht lange auf Ihre Ladeaufgabe gewartet hat. Sie müssen eine espresso-idling-resource verwenden, um es zu warten, bis diese Aufgabe beendet ist.

Dann brauchen Sie eine Klasse zu implementieren IdlingResource und deklarieren Sie Ihre Aktivität.

Wenn Ihr Espresso-Test läuft, wird er Ihre langwierige Aufgabe kennen und warten, um das Ergebnis zu testen.

Fügen Sie zunächst ihre Abhängigkeit hinzu.

compile "com.android.support.test.espresso:espresso-idling-resource:2.2.2" 

Zweitens benötigen Sie zwei Java-Dateien im Ordner src/main/java/your-package.
SimpleCountingIdlingResource.java

public final class SimpleCountingIdlingResource implements IdlingResource { 

    private final String mResourceName; 

    private final AtomicInteger counter = new AtomicInteger(0); 

    // written from main thread, read from any thread. 
    private volatile ResourceCallback resourceCallback; 

    /** 
    * Creates a SimpleCountingIdlingResource 
    * 
    * @param resourceName the resource name this resource should report to Espresso. 
    */ 
    public SimpleCountingIdlingResource(String resourceName) { 
    mResourceName = checkNotNull(resourceName); 
    } 

    @Override public String getName() { 
    return mResourceName; 
    } 

    @Override public boolean isIdleNow() { 
    return counter.get() == 0; 
    } 

    @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 
    this.resourceCallback = resourceCallback; 
    } 

    /** 
    * Increments the count of in-flight transactions to the resource being monitored. 
    */ 
    public void increment() { 
    counter.getAndIncrement(); 
    } 

    /** 
    * Decrements the count of in-flight transactions to the resource being monitored. 
    * 
    * If this operation results in the counter falling below 0 - an exception is raised. 
    * 
    * @throws IllegalStateException if the counter is below 0. 
    */ 
    public void decrement() { 
    int counterVal = counter.decrementAndGet(); 
    if (counterVal == 0) { 
     // we've gone from non-zero to zero. That means we're idle now! Tell espresso. 
     if (null != resourceCallback) { 
     resourceCallback.onTransitionToIdle(); 
     } 
    } 

    if (counterVal < 0) { 
     throw new IllegalArgumentException("Counter has been corrupted!"); 
    } 
    } 
} 

EspressoIdlingResource.java

public class EspressoIdlingResource { 

    private static final String RESOURCE = "GLOBAL"; 

    private static SimpleCountingIdlingResource mCountingIdlingResource = 
     new SimpleCountingIdlingResource(RESOURCE); 

    public static void increment() { 
    mCountingIdlingResource.increment(); 
    } 

    public static void decrement() { 
    mCountingIdlingResource.decrement(); 
    } 

    public static IdlingResource getIdlingResource() { 
    return mCountingIdlingResource; 
    } 
} 

Ok. Lass uns zur Aktivität gehen, wo du eine zeitraubende Aufgabe hast. Erstens, legen Sie diese Methode an der Unterseite.

@VisibleForTesting 
    public IdlingResource getCountingIdlingResource() { 
     return EspressoIdlingResource.getIdlingResource(); 
    } 

In Ihrer zeitraubenden Aufgabe. Sie sollten Ihrem Espresso sagen, dass er so warten soll.

EspressoIdlingResource.increment(); 

    yourTask.run(new Callback() { 
    void onFinish(){ 
     EspressoIdlingResource.decrement(); 
    } 
    }) 

Der letzte Schritt besteht darin, diese Methoden in Ihrer UI-Testklasse zu definieren.

@Before 
public void registerIdlingResource() { 
    Espresso.registerIdlingResources(mOnBoardActivityTestRule.getActivity().getCountingIdlingResource()); 
} 

/** 
* Unregisters your idling resource so it can be garbage collected and does not leak any memory 
*/ 
@After 
public void unregisterIdlingResource() { 
    Espresso.unregisterIdlingResources(mOnBoardActivityTestRule.getActivity().getCountingIdlingResource()); 
} 

Ja. Endlich sind wir fertig.

Verwandte Themen