espresso_用于RecyclerView数据更改的Espresso空闲资源

本文探讨了在Android Espresso测试中处理RecyclerView数据变化的问题。详细介绍了如何使用IdlingResource监听RecyclerView布局完成,确保测试在数据更新后正确执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

espresso

更新数据时,使用Android Espresso测试RecyclerView时遇到问题。

这是针对Android应用程序,其中RecyclerView显示联系人列表。 操作栏中有一个SearchView ,可以过滤联系人列表以显示匹配的联系人姓名。

Espresso测试运行如下:

  • 开始活动。
  • Espresso验证联系人的完整列表是否显示在RecyclerView中。 这很好。
  • 在SearchView中输入查询字符串,然后启动RecyclerView中的数据过滤(我使用SearchView来获取查询字符串,但是可以使用诸如EditText等其他控件代替)。
  • Espresso验证联系人列表已更改为仅显示匹配项。 失败
 @RunWith (AndroidJUnit4. class )  public class RecyclerViewIdlingResourceTest {  @Rule  public ActivityRule<MainActivity> activityRule = ActivityRule<MainActivity> activityRule = new ActivityRule<MainActivity>(MainActivity. class );  // number of items in the original list  int allItemsCount = ...;  // number of items after the list has been filtered  int filteredItemsCount = ...;  @Test  public void testRecyclerviewFilter()  { 
   // verify all test items loaded 
   // SUCCESS 
   onView(withId(R.id.recyclerview)).check(withItemCount(allItemsCount)); 
   // since the search view is initially collapsed, open it first before    tests are run 
   onView(withId(R.id.action_search)).perform(click()); 
   // enter some text into the search view, and then press the action button. 
   String searchText = "test" 
   onView(withId(android.support.design.R.id.search_src_text)).perform(typeText(searchText), pressImeActionButton()); 
   // verify the number of items in the recyclerview list has been altered 
   // FAIL! 
   onView(withId(R.id.recyclerview)).check(withItemCount(filteredItemsCount));  }  } 

不幸的是,似乎Espresso断言要验证项目列表是否已更改,发生在RecyclerView完成重新加载更新的数据并重新绘制自身之前。 因此,当发现RecyclerView仍然具有原始项目数时,该测试将失败,因为它尚未使用新的数据列表重新绘制自身。

这篇文章的代码在本要点中 。 它采用不完整代码的形式,仅包含与帖子相关的内容。 另外,还有多种实现和过滤RecyclerView的方法,因此我将把这一部分留给读者。

所以有什么问题?

更改RecyclerView的数据后,我在适配器上调用notifyDataSetChanged()

基于这个StackOverflow问题 ,问题似乎是当调用notifyDataSetChanged()时,它仅使RecyclerView中的数据无效,而没有立即更新该小部件。 因此,我怀疑Espresso断言是在RecyclerView更新之前发生的。

为了对此进行测试,我在Espresso断言之前引入了一个暂停,以允许RecyclerView有时间更新,并且测试通过了。

 @Test  public void testRecyclerviewFilterWithPause()  { 
   // verify all test items loaded 
   // SUCCESS 
   onView(withId(R.id.recyclerview)).check(withItemCount(allItemsCount)); 
   // since the search view is initially collapsed, open it first before tests are run 
   onView(withId(R.id.action_search)).perform(click()); 
   // enter some text into the search view, and then press the action button. 
   String searchText = "test" 
   onView(withId(android.support.design.R.id.search_src_text)).perform(typeText(searchText), pressImeActionButton()); 
   // pause for arbitrary period of time, InterruptedException handling left out to simplify example 
   Thread.sleep( 1000 ); 
   // verify the number of items in the recyclerview list has been altered 
   // SUCCESS - assuming the pause time was long enough 
   onView(withId(R.id.recyclerview)).check(withItemCount(filteredItemsCount));  } 

在这里,我使用的是Thread.sleep(),但是任何具有Handlers的Android等都可以做到。 当然,这全都是小技巧。 建议在Espresso继续测试之前引入等待某些过程完成的方法是使用Idling Resources

在某个任意时间段使用暂停并不理想,因为它经常导致不稳定的测试或使测试的运行时间超出必要。

RecyclerView回调

为了使空闲资源正常工作,它需要知道RecyclerView RecyclerView何时使用新的列表数据进行重绘。

RecyclerView(及其支持类)具有各种回调,这些回调可以表明RecyclerView正在重绘过程​​中。 在StackOverflow上搜索之后,我发现了以下可能性:

我决定使用onGlobalLayoutListener ,但是RecyclerView似乎有多种方式来表示它已被重绘。

空闲资源正在侦听...

首先,我们需要一些接口用作回调,以在RecyclerView,包含RecyclerView和空闲资源的活动/片段之间进行通信。

首先,这里是RecyclerView的接口,用于在使用新数据进行重绘过程时通知活动。

 public interface RecyclerViewIdlingCallback {  public void setRecyclerViewLayoutCompleteListener(RecyclerViewLayoutCompleteListener listener);  public void removeRecyclerViewLayoutCompleteListener(RecyclerViewLayoutCompleteListener listener);  // Callback for the idling resource to check if the resource (in this example the activity containing the recyclerview)  // is idle  public boolean isRecyclerViewLayoutCompleted();  } 

然后,另一个接口用作活动的回调,以将空闲资源通知..空闲。

 public interface RecyclerViewLayoutCompleteListener {  // Callback to notify the idling resource that it can transition to the idle state  public void onLayoutCompleted();  } 

这是一个示例活动,仅显示与空闲资源一起使用的相关代码。

 public class RecyclerViewCallbackContactsActivity extends AppCompatActivity implements 
   SearchView.OnQueryTextListener, 
   ViewTreeObserver.OnGlobalLayoutListener, 
   RecyclerViewIdlingCallback { 
   /** 
    * Flag to indicate if the layout for the recyclerview has complete. This should only be used 
    * when the data in the recyclerview has been changed after the initial loading. 
    */ 
   private boolean recyclerViewLayoutCompleted; 
   /** 
    * Listener to be set by the idling resource, so that it can be notified when recyclerview 
    * layout has been done. 
    */ 
   private RecyclerViewLayoutCompleteListener listener; 
   @Override 
   public void onCreate (Bundle savedInstanceState) { 
     super .onCreate(savedInstanceState); 
     // CODE HERE to initialize the recyclerview 
     recyclerViewLayoutCompleted = true ; 
     recyclerView.getViewTreeObserver().addOnGlobalLayoutListener( this ); 
   } 
   @Override 
   public boolean onQueryTextSubmit(String query) { 
     // CODE HERE to filter the recyclerview using the query string, 
     // - this should eventually result in notifyDataSetChanged() being called on the adapter         
     // flag that a new layout will be required with the filtered data 
     recyclerViewLayoutCompleted = false ; 
   } 
   @Override 
   public void onGlobalLayout() { 
     if (listener != null ) 
     { 
       // set flag to let the idling resource know that processing has completed and is now idle 
       recyclerViewLayoutCompleted = true ; 
       // notify the listener (should be in the idling resource) 
       listener.onLayoutCompleted(); 
     } 
   } 
   @Override 
   public boolean isRecyclerViewLayoutCompleted() { 
     return recyclerViewLayoutCompleted; 
   } 
   @Override 
   public void setRecyclerViewLayoutCompleteListener(RecyclerViewLayoutCompleteListener listener) { 
     this .listener = listener; 
   } 
   @Override 
   public void removeRecyclerViewLayoutCompleteListener(RecyclerViewLayoutCompleteListener listener) { 
     if ( this .listener != null && this .listener == listener) 
     { 
       this .listener = null ; 
     } 
   }  } 

该活动的重要部分是:

  • 侦听器方法onGlobalLayout(),该信号指示recyclerview夸大了其布局以进行重绘
  • 布尔标志recyclerViewLayoutCompleted ,由空闲资源使用,以检查recyclerview重绘后Espresso测试是否可以继续运行。

这是用于在活动中测试recyclerview的空闲资源。

 public class RecyclerViewLayoutCompleteIdlingResource implements IdlingResource { 
   private ResourceCallback resourceCallback; 
   private RecyclerViewIdlingCallback recyclerViewIdlingCallback; 
   private RecyclerViewLayoutCompleteListener listener; 
   public RecyclerViewLayoutCompleteIdlingResource(RecyclerViewIdlingCallback recyclerViewIdlingCallback){ 
     this .recyclerViewIdlingCallback = recyclerViewIdlingCallback; 
     listener = new RecyclerViewLayoutCompleteListener() { 
       @Override 
       public void onLayoutCompleted() { 
         if (resourceCallback == null ){ 
           return ; 
         } 
         if (listener != null ) { 
           recyclerViewIdlingCallback.removeRecyclerViewLayoutCompleteListener(listener); 
         } 
         //Called when the resource goes from busy to idle. 
         resourceCallback.onTransitionToIdle(); 
       } 
     }; 
     // add the listener to the view containing the recyclerview 
     recyclerViewIdlingCallback.setRecyclerViewLayoutCompleteListener (listener); 
   } 
   @Override 
   public String getName() { 
     return "RecyclerViewLayoutCompleteIdlingResource" ; 
   } 
   @Override 
   public boolean isIdleNow() { 
     return recyclerViewIdlingCallback.isRecyclerViewLayoutCompleted(); 
   } 
   @Override 
   public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 
     this .resourceCallback = resourceCallback; 
   }  } 

该活动作为RecyclerViewIdlingCallback实现传递到其构造函数中的空闲资源。 然后,当活动中的recyclerview准备就绪时,活动将在空闲资源中调用回调以指示它为“空闲”。

最后,我们可以将其合并到Espresso测试中。

 @Test  public void testFilterRecyclerViewUsingSearchView()  { 
   // CODE HERE use espresso to use the SearchView to filter the recyclerview 
   RecyclerViewLayoutCompleteIdlingResource idlingResource = new RecyclerViewLayoutCompleteIdlingResource((RecyclerViewCallbackContactsActivity) activityTestRule.getActivity()); 
   IdlingRegistry.getInstance().register(idlingResource); 
   // CODE HERE to verify the recyclerview with the updated data 
   IdlingRegistry.getInstance().unregister(idlingResource);  } 

警告
我实际上是前一段时间开始写这篇文章的,所以代码是从Android支持库而不是从Androidx库用于Recyclerview的

翻译自: https://www.javacodegeeks.com/2019/08/espresso-idling-resource-recyclerview-data-changes.html

espresso

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值