You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中RecyclerView及其适配器的Mockito单元测试问题

解决RecyclerView适配器Mockito单元测试的空指针问题

Hey there! 作为Android新手刚接触Mockito和RecyclerView单元测试碰到空指针太正常了,我来帮你捋捋常见的坑和靠谱的解决思路~

首先得明确:单元测试的核心是测逻辑,不是测Android系统组件的渲染。你不用硬去mock整个RecyclerView再绑定适配器——RecyclerView内部有一堆依赖(比如LayoutManager、RecycledViewPool),mock这些很容易因为没覆盖到内部调用触发空指针。咱们换个思路,把适配器和系统组件解耦来测。

一、先搞清楚你要测什么

先把测试目标拆成小模块,比如:

  • 适配器的item数量是否和数据源匹配
  • item点击/长按事件是否正确触发回调
  • 数据更新后适配器是否正确刷新UI

二、常见空指针的原因&解决

你之前mock Activity和RecyclerView后出现空指针,大概率是因为:

  • Mock的RecyclerView没有覆盖内部方法(比如getContext()、getLayoutManager()),适配器绑定的时候调用了这些方法导致空指针
  • 适配器依赖了Activity里未mock的对象(比如Context、资源),初始化时出问题

解决办法:单独测试适配器,完全不依赖RecyclerView或Activity的真实/ mock实例

三、实战测试例子

1. 测试item点击事件

假设你的适配器是这样的(带点击回调):

class MyAdapter(
    private val itemList: List<String>,
    private val onItemClick: (String) -> Unit
) : RecyclerView.Adapter<MyAdapter.ItemViewHolder>() {

    inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: String) {
            itemView.setOnClickListener { onItemClick(item) }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_layout, parent, false)
        return ItemViewHolder(view)
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        holder.bind(itemList[position])
    }

    override fun getItemCount() = itemList.size
}

测试点击的话,不用碰RecyclerView,直接mock关键对象就行:

import org.junit.Test
import org.mockito.Mockito.*

class MyAdapterTest {

    @Test
    fun `item click triggers callback with correct data`() {
        // 1. 准备测试数据
        val testItems = listOf("Apple", "Banana", "Cherry")
        // 2. Mock点击回调接口
        val mockClickCallback = mock<(String) -> Unit>()
        // 3. 初始化适配器
        val adapter = MyAdapter(testItems, mockClickCallback)

        // 4. Mock一个ViewGroup(onCreateViewHolder需要它)和View
        val mockParent = mock<ViewGroup>()
        val mockContext = mock<Context>()
        `when`(mockParent.context).thenReturn(mockContext)
        val mockItemView = mock<View>()
        `when`(LayoutInflater.from(mockContext).inflate(anyInt(), eq(mockParent), eq(false)))
            .thenReturn(mockItemView)

        // 5. 创建ViewHolder并绑定数据
        val viewHolder = adapter.onCreateViewHolder(mockParent, 0)
        adapter.onBindViewHolder(viewHolder, 1) // 绑定第2个item(Banana)

        // 6. 模拟点击itemView
        mockItemView.performClick()

        // 7. 验证回调是否被正确调用,参数是Banana
        verify(mockClickCallback).invoke("Banana")
    }
}

2. 测试数据源更新

如果你的适配器有更新数据的方法,比如:

fun updateItems(newItems: List<String>) {
    itemList = newItems
    notifyDataSetChanged()
}

测试这个方法的逻辑:

@Test
fun `update items changes adapter item count`() {
    val initialItems = listOf("A", "B")
    val adapter = MyAdapter(initialItems, {})

    assertEquals(2, adapter.itemCount)

    val newItems = listOf("X", "Y", "Z")
    adapter.updateItems(newItems)

    assertEquals(3, adapter.itemCount)
}

四、如果一定要测Activity和RecyclerView的交互

如果你想验证Activity是否正确给RecyclerView设置了适配器,建议用Robolectric(而不是纯Mockito),因为它能模拟Android环境,创建真实的Activity和View实例,避免mock系统组件的空指针:

import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.LooperMode

@RunWith(RobolectricTestRunner::class)
@LooperMode(LooperMode.Mode.PAUSED)
class MainActivityTest {

    @Test
    fun `activity sets recycler view adapter correctly`() {
        // 创建Activity实例
        val activity = Robolectric.buildActivity(MainActivity::class.java).create().resume().get()
        // 获取RecyclerView
        val recyclerView = activity.findViewById<RecyclerView>(R.id.recycler_view)
        
        // 验证适配器不为空,且item数量正确
        assertNotNull(recyclerView.adapter)
        assertEquals(5, recyclerView.adapter?.itemCount)
    }
}

最后小贴士

  • 单元测试要聚焦单一职责:适配器的逻辑就单独测适配器,别把Activity、RecyclerView都混在一起,这样测试更稳定,出问题也更容易定位
  • 尽量避免mock Android系统组件(比如RecyclerView、Context),能用真实实例(比如Robolectric提供的)或者解耦依赖就尽量不用mock

内容的提问来源于stack exchange,提问作者Niraj Kulal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:11:54