'How to test(tdd) an element inside child recycler view?

add to cart errorI've used a recycler view inside another recycler view. While using test-driven development, I was trying to test the click functionality of add to cart button code as follows: MenuDishAdapter- child recycler view MenuAdapter- parent recycler view

class MenuAdapter(private val context: Context, val menuList:List<MenuData>, val menuActivity: MenuActivity):RecyclerView.Adapter<MenuAdapter.MenuViewHolder>() {
    class MenuViewHolder(private val itemBinding:RvDishGroupBinding):RecyclerView.ViewHolder(itemBinding.root) {
        val dishGroupBinding:RvDishGroupBinding = itemBinding

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MenuViewHolder = MenuViewHolder(
        RvDishGroupBinding.inflate(LayoutInflater.from(parent.context), parent, false))

    @SuppressLint("CutPasteId")
    override fun onBindViewHolder(holder: MenuViewHolder, position: Int) {
       val getPos= menuList[position]
        holder.dishGroupBinding.dishCatgTypeMtv.text=getPos.categoryType
        val adapter = MenuDishAdapter(context, menuList[position].categoryDish, menuActivity, )
        holder.dishGroupBinding.dishLl.adapter= adapter
        /*Logger.i("Menu list size ${menuList.size}")
        Logger.i("Menu list $menuList")*/
        if(position==menuList.size-1)
        {
            holder.dishGroupBinding.rvGroupSeparatorVw.visibility= View.GONE
        }
    }

    override fun getItemCount(): Int = menuList.size
}
class MenuDishAdapter(
    private val context: Context,
    val menuList: List<DishData>,
    val menuActivity: MenuActivity
) :
    RecyclerView.Adapter<MenuDishAdapter.MenuDishViewHolder>() {
    class MenuDishViewHolder(private val dishItem: RvDishItemBinding) :
        RecyclerView.ViewHolder(dishItem.root) {
        val dishItemBinding = dishItem

    }

    var isFavourite: Boolean = false

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MenuDishViewHolder =
        MenuDishViewHolder(
            RvDishItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        )

    override fun onBindViewHolder(holder: MenuDishViewHolder, position: Int) {

        val getPos= menuList[position]
        Glide.with(context).load(getPos.dishImage).placeholder(R.drawable.img_dessert).into( holder.dishItemBinding.rvDishImgIv)
        holder.dishItemBinding.rvDishNameMtv.text= getPos.dishName
       if(getPos.dishDescription.isNullOrEmpty())
       {
           holder.dishItemBinding.rvDishDescriptionMtv.visibility=View.GONE
       }
        else
       {
           holder.dishItemBinding.rvDishDescriptionMtv.text= getPos.dishDescription
       }
        holder.dishItemBinding.rvDishPriceMtv.text= "$"+ getPos.dishPrice
        if(!getPos.isVeg)
        {
            holder.dishItemBinding.rvDishTypeIv.backgroundTintList=
                ContextCompat.getColorStateList(menuActivity, R.color.red)
        }
        holder.dishItemBinding.rvDishCustomizationMtv.text= context.getString(R.string.your_customisations_are)+" Radish, Mushroom, carrots, turnip, radish"

        holder.dishItemBinding.rvDishFavMarkIb.setOnClickListener {
            if(isFavourite)
            {
                holder.dishItemBinding.rvDishFavMarkIb.setBackgroundResource(R.drawable.ic_unfav)
                Toast.makeText(context, "${getPos.dishName} removed from Favourite. ", Toast.LENGTH_SHORT).show()
                notifyDataSetChanged()
                isFavourite= false
            }
            else
            {
                holder.dishItemBinding.rvDishFavMarkIb.setBackgroundResource(R.drawable.ic_fav)
                Toast.makeText(context, "${getPos.dishName} marked Favourite. ", Toast.LENGTH_SHORT).show()
                notifyDataSetChanged()
                isFavourite=true
            }
        }

        holder.dishItemBinding.rvAddToCartBtn.setOnClickListener {
            Utils.printShortToast(context, "${getPos.dishName} added to the cart")
        }
        holder.dishItemBinding.rvDishCustomizeBtn.setOnClickListener {
            Utils.printShortToast(context, "${getPos.dishName} customised")
        }
    }

    override fun getItemCount(): Int = menuList.size
}

XML for MenuDishAdapter

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="133dp"
        android:layout_marginStart="6dp"
        android:layout_marginTop="5dp"
        android:layout_marginEnd="6dp"
        android:id="@+id/rv_dish_item_ll">
    <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/detail_parent_cl"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <androidx.cardview.widget.CardView
                android:id="@+id/rv_dish_img_cv"
                android:layout_width="109dp"
                android:layout_height="104dp"
                android:elevation="5dp"
                app:cardCornerRadius="8dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent">

            <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/rv_dish_img_iv"
                    android:layout_width="109dp"
                    android:layout_height="104dp"
                    android:scaleType="fitXY"
                    tools:src="@drawable/img_fav_2" />

        </androidx.cardview.widget.CardView>

        <androidx.appcompat.widget.LinearLayoutCompat
                android:id="@+id/dish_details_ll"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_marginStart="8dp"
                android:layout_marginEnd="0dp"
                android:orientation="vertical"
                android:weightSum="5"
                app:layout_constraintBottom_toBottomOf="@id/rv_dish_img_cv"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@id/rv_dish_img_cv"
                app:layout_constraintTop_toTopOf="@id/rv_dish_img_cv">

            <androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/dish_name_cl"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="0dp"
                    android:layout_weight="1"
                    android:orientation="horizontal">

                <com.google.android.material.textview.MaterialTextView
                        android:id="@+id/rv_dish_name_mtv"
                        style="@style/TextAppearance.MdcTypographyStyles.Body22"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="0dp"
                        android:layout_marginEnd="8dp"
                        android:ellipsize="end"
                        android:gravity="start"
                        android:maxLines="1"
                        android:textColor="@color/black"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintEnd_toStartOf="@id/rv_dish_type_iv"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        tools:text="Masala Noodles" />

                <androidx.appcompat.widget.AppCompatImageView
                        android:id="@+id/rv_dish_type_iv"
                        android:layout_width="21dp"
                        android:layout_height="18dp"
                        android:layout_gravity="end"
                        android:background="@drawable/ic_veg_dish"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

            </androidx.constraintlayout.widget.ConstraintLayout>

            <androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/detail_middle_cl"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="2"
                    android:orientation="horizontal">

                <LinearLayout
                        android:id="@+id/text_ll"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintEnd_toStartOf="@id/rv_dish_fav_mark_ib"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent">

                    <com.google.android.material.textview.MaterialTextView
                            android:id="@+id/rv_dish_description__mtv"
                            style="@style/TextAppearance.MdcTypographyStyles.Body12"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:maxLines="2"
                            android:layout_marginEnd="2dp"
                            android:layout_marginStart="0dp"
                            android:textAlignment="textStart"
                            android:textColor="@color/black"
                            tools:text="@string/description" />

                    <com.google.android.material.textview.MaterialTextView
                            android:id="@+id/rv_dish_price_mtv"
                            style="@style/TextAppearance.MdcTypographyStyles.Body22"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:maxLines="1"
                            android:textAlignment="textStart"
                            tools:text="$10.5" />

                </LinearLayout>

                <androidx.appcompat.widget.AppCompatImageButton
                        android:id="@+id/rv_dish_fav_mark_ib"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_weight="1"
                        android:clickable="true"
                        android:elevation="5dp"
                        android:focusable="true"
                        android:paddingStart="10dp"
                        android:paddingTop="0dp"
                        android:paddingEnd="0dp"
                        android:paddingBottom="5dp"
                        android:background="@drawable/ic_unfav"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

            </androidx.constraintlayout.widget.ConstraintLayout>

            <androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/buttons_cl"
                    android:layout_width="match_parent"
                    android:layout_height="23dp"
                    android:layout_marginTop="2dp"
                    android:paddingBottom="1dp"
                    android:paddingTop="0dp"
                    android:layout_weight="1"
                    android:orientation="horizontal">

                <androidx.appcompat.widget.AppCompatButton
                        android:id="@+id/rv_add_to_cart_btn"
                        style="@style/TextAppearance.MdcTypographyStyles.Button1"
                        android:layout_width="109dp"
                        android:layout_height="0dp"
                        android:layout_marginStart="0dp"
                        android:layout_marginEnd="2dp"
                        android:background="@drawable/bg_button"
                        android:backgroundTint="@color/primaryColor"
                        android:drawableStart="@drawable/ic_button_cart"
                        android:drawablePadding="0dp"
                        android:focusable="true"
                        android:padding="2dp"
                        android:paddingStart="5dp"
                        android:paddingEnd="5dp"
                        android:text="@string/add_to_cart"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                <androidx.appcompat.widget.AppCompatButton
                        android:id="@+id/rv_dish_customize_btn"
                        style="@style/TextAppearance.MdcTypographyStyles.Button1"
                        android:layout_width="109dp"
                        android:layout_height="0dp"
                        android:layout_marginStart="2dp"
                        android:layout_marginEnd="0dp"
                        android:background="@drawable/bg_button"
                        android:backgroundTint="@color/black"
                        android:focusable="true"
                        android:padding="2dp"
                        android:text="@string/customize"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />


            </androidx.constraintlayout.widget.ConstraintLayout>


        </androidx.appcompat.widget.LinearLayoutCompat>

        <com.google.android.material.textview.MaterialTextView
                android:id="@+id/rv_dish_customization_mtv"
                style="@style/TextAppearance.MdcTypographyStyles.Body12"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:layout_marginBottom="3dp"
                android:ellipsize="end"
                android:maxLines="1"
                android:text="@string/your_customisations_are"
                android:textAlignment="textStart"
                android:textColor="@color/black"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="@id/dish_details_ll"
                app:layout_constraintStart_toStartOf="@id/rv_dish_img_cv"
                app:layout_constraintTop_toBottomOf="@id/rv_dish_img_cv"
                tools:text="Your customisations are - Onion, Tomato, Mushroom, cucumber, radish" />
    </androidx.constraintlayout.widget.ConstraintLayout>


</LinearLayout>

Now while testing the add to cart button the test case is as follows: **Class name: MenuActivityTest see code commented in testingMenuList() **

@RunWith(AndroidJUnit4::class)
@LargeTest
class MenuActivityTest {

    @get:Rule
    var rule = ActivityScenarioRule(MenuActivity::class.java)


    //test to check the visibility of the menu screen layout on the device
    @Test
    fun checkScreenDisplay() {
        onView(withId(R.id.menu_cl))
            .check(matches(isDisplayed()))

    }

    @Test
    fun testingMenuList() {
        //testing whether the recycler view is displayed for not
         onView(withId(R.id.menu_rv)).check(matches(isDisplayed()))
         onView(allOf(withId(R.id.dish_ll), isDisplayed()))


         //testing the scroll to position to 3rd position
         onView(withId(R.id.menu_rv)).perform(
             RecyclerViewActions
                 .scrollToPosition<RecyclerView.ViewHolder>(3)
         )

         //testing a particular item of the list
         onView(allOf(withId(R.id.dish_catg_type_mtv), withText("Beverages")))
         onView(allOf(withId(R.id.rv_dish_name_mtv), withText("Coffee")))
         onView(allOf(withId(R.id.rv_dish_price_mtv), withText("0")))
         onView(
             allOf(
                 withId(R.id.rv_dish_description__mtv),
                 withText("Because ground coffee beans, coffee roasts, and brewing methods vary, so do coffee drinks. They come in so many different varieties and flavors, such as black coffee, espresso, latte, cappuccino, or mocha.")
             )
         )

        /*onView(allOf(isDisplayed(), withId(R.id.rv_add_to_cart_btn)))
            .perform(RecyclerViewActions.actionOnItemAtPosition<MenuDishAdapter.MenuDishViewHolder>(2, click()));
*/

        /*val result = onView(
            allOf(
                withId(R.id.rv_add_to_cart_btn),
                withText("Coffee")
            )
        ).perform(
            click()
        )

        Utils.printErrorLog("Result is: $result")*/
       
        onView(allOf(withId(R.id.rv_add_to_cart_btn), isDescendantOfA(allOf(withId(R.id.buttons_cl))))).check(matches(isFocusable()))
*/

       /* onData(allOf(withId(R.id.rv_add_to_cart_btn)))
            .inAdapterView(allOf(withId(R.id.rv_dish_item_ll)))
            .atPosition(1)
            .perform(click())*/

    }
}

Each and every time in running the testingMenuList I'm getting the error(screenshot attached). What is the correct method to check the add to cart button click functionality? The flow is as: MenuActivity has a recycler view menu_rv and this menu_rv contains another recycler view name dish_ll. Inside dish_ll, rv_add_to_cart_btn is the button that is being tested using the TDD approach.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source