Android RecyclerView适配器在单元测试中返回0的项数

10

我正在尝试使用AndroidJunit4测试RecyclerView,以下是我的测试代码:

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


    }

以下是我的Activity中 displayProducts() 方法的代码:

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

它正在报错,错误信息如下:

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)

请帮忙看看我的代码有什么问题?


你如何设置 mAdapter - tynn
1
动画在UI测试中是你的敌人,每个进度条、自定义动画或在非异步线程池上进行的任何工作都不会被Espresso注册,它只会运行其他断言,因为它认为没有需要等待的内容。另外,尽量在设置和拆卸过程中编写你的测试,这样你的活动在构建过程中就会有数据。你在displayProducts()之后调用了notifyDataSetChanged()吗? - originx
mAdapter来自哪里?我认为您正在检查错误的适配器。 - Tudor
2个回答

0
原因是您的Espresso测试没有等待耗时的加载任务。您需要使用espresso-idling-resource来告诉它等待该任务完成。
然后,您需要实现一个类来实现IdlingResource并在您的Activity中声明它。
当您的Espresso测试运行时,它将知道并等待您的长时间耗时任务完成,并测试结果。
首先,添加它的依赖项。
 compile "com.android.support.test.espresso:espresso-idling-resource:2.2.2"

其次,在src/main/java/your-package文件夹中,您需要两个Java文件。 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;
  }
}

好的。让我们去到有耗时任务的Activity。 首先,将这个方法放在底部。

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

在你耗时的任务中,你应该这样告诉Espresso等待。

EspressoIdlingResource.increment();

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

最后一步是在您的UI测试类中定义这些方法。

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

是的,终于完成了。


-1

我看到一个问题,就是在主/UI线程更新列表之前你正在查询列表的大小。因此,你需要在当前线程中等待,直到Activity在主线程上完成列表的更新。

你可以这样做,

Thread.sleep(500);

在Test类中等待,测试Activity中的列表设置行为,您会发现断言是有效的。
由于主线程无限运行直到应用程序运行结束,因此您必须实现Activity提供的回调接口以便在列表填充完成时得到通知。

问题是为什么Espresso不等待MessageQueue为空? - azizbekian
@azizbekian 因为它正在另一个线程(后台)上运行。添加观察者设计模式并在发生的确切时间捕获它,否则使用Thread sleep方法。 - Avi Levin
你的解释不正确:Espresso 运行在后台线程上,但是它会监听 UI 线程的 MessageQueue 是否为空,只有在它为空的时候,Espresso 才会继续执行匹配器。你的回答提供了一个解决方法,但没有说明问题发生的原因。 - azizbekian
那么实际原因是什么,以及正确的解决方法是什么?@azizbekian - dev_android
我无法提供令人满意的解释,这就是为什么我没有发布答案的原因。根据您提供的代码,这应该可以工作,因为Espresso应该知道这些更改,并且应该等待直到从“MessageQueue”中消耗所有消息。 - azizbekian
这个东西在测试中永远不应该被使用,因为它会带来不稳定性,特别是在不同的设备上(想象一下在某个古老的三星Galaxy Ace手机上运行这段代码,还要和其他应用程序混杂在一起)。你应该使用空闲资源来告诉你的测试等待网络、数据库调用等操作完成。 - originx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接