简体   繁体   中英

Android RecyclerView Adapter Item count is returning 0 on unit testing

I am trying to test RecyclerView with AndroidJunit4, it is my test code:

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


    }

Here is my code for displayProducts() in Activity:

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

It is giving error like:

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)

Please help what is the problem in my code?

The reason is that your Espresso test did not wait your loading task which is time-consuming. You need to use a espresso-idling-resource to tell it to wait this task to finish.

Then you need a class to implement IdlingResource and declare it your Activity.

When your Espresso test run, it will know and wait your long-time consuming task to complete and test the result.

Firstly, add its dependency.

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

Secondly, you need two Java files in folder 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. Let's go to Activity where you have a time-consuming task. Firstly, put this method at the bottom.

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

Inside your time-consuming task. you should tell your Espresso to wait like this.

EspressoIdlingResource.increment();

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

Final step is to define these methods in your UI test class.

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

Yeah. Finally we done.

There is one problem I can see here, your are inquiring the List size before the Main/UI thread is able to update it. So, you will have to wait in the current thread till the Activity finished updating the list on Main thread.

You can do,

Thread.sleep(500);

in the Test class to wait, to test the list setting behavior in Activity and you will find the assertion to be valid.

Since, the main thread runs infinitely till the application is running, you will have to implement a callback interface provided by the Activity to be informed about when populating the list is finished.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM