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

ListView+Filterable适配的Espresso测试:如何统一注册IdlingResource?

我之前也遇到过这个坑——Espresso默认只盯着主线程的操作,而Filter的performFiltering是在后台线程跑的,所以测试经常会在过滤完成前就执行断言,导致失败。要全局解决这个问题,咱们可以搞一个通用的IdlingResource来跟踪所有Filter的执行状态,不用每个Filter都单独写等待逻辑,一步到位。

实现全局Filter IdlingResource的步骤

1. 编写全局的Filter状态跟踪IdlingResource

先写一个FilterIdlingResource类,实现Espresso的IdlingResource接口,用来统一跟踪所有正在执行的Filter:

class FilterIdlingResource : IdlingResource {
    // 用原子类保证线程安全(Filter的performFiltering在后台线程,publishResults在主线程)
    private val activeFilters = AtomicInteger(0)
    private var resourceCallback: IdlingResource.ResourceCallback? = null

    // 当Filter开始执行时调用
    fun incrementActiveFilters() {
        activeFilters.incrementAndGet()
    }

    // 当Filter执行完成时调用
    fun decrementActiveFilters() {
        val count = activeFilters.decrementAndGet()
        if (count == 0) {
            resourceCallback?.onTransitionToIdle()
        }
    }

    override fun getName(): String = "FilterIdlingResource"

    override fun isIdleNow(): Boolean = activeFilters.get() == 0

    override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
        this.resourceCallback = callback
    }
}

2. 封装BaseFilter统一处理状态上报

为了不用修改每个Filter的逻辑,咱们写一个BaseFilter基类,让项目里所有实现Filterable的适配器的Filter都继承它,自动把Filter的执行状态上报给上面的IdlingResource:

abstract class BaseFilter(private val idlingResource: FilterIdlingResource) : Filter() {
    override fun performFiltering(constraint: CharSequence?): FilterResults {
        // 通知IdlingResource有Filter开始执行
        idlingResource.incrementActiveFilters()
        return try {
            // 子类实现实际过滤逻辑
            onPerformFiltering(constraint)
        } finally {
            // 注意:performFiltering在后台线程,不能直接decrement,要等主线程的publishResults完成后再减
        }
    }

    override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
        try {
            // 子类实现结果发布逻辑
            onPublishResults(constraint, results)
        } finally {
            // 通知IdlingResourceFilter执行完成
            idlingResource.decrementActiveFilters()
        }
    }

    // 子类需要实现的实际过滤方法
    abstract fun onPerformFiltering(constraint: CharSequence?): FilterResults

    // 子类需要实现的结果发布方法
    abstract fun onPublishResults(constraint: CharSequence?, results: FilterResults?)
}

3. 全局注册IdlingResource

在你的测试基类或者每个测试类的@Before方法里,注册这个IdlingResource到Espresso,同时把它传递给所有适配器的BaseFilter:

class ListViewFilterTest {
    private val filterIdlingResource = FilterIdlingResource()

    @Before
    fun setUp() {
        // 注册IdlingResource,让Espresso感知Filter的执行状态
        Espresso.registerIdlingResources(filterIdlingResource)
        // 把IdlingResource传递给适配器的Filter(根据你的项目结构调整,比如通过依赖注入或者全局实例获取)
        // 示例:yourListViewAdapter.filter = object : BaseFilter(filterIdlingResource) { ... }
    }

    @After
    fun tearDown() {
        // 测试结束后注销,避免影响其他测试用例
        Espresso.unregisterIdlingResources(filterIdlingResource)
    }

    // 测试过滤逻辑
    @Test
    fun whenFilteringListView_shouldShowCorrectResults() {
        // 执行过滤操作,比如输入搜索文本
        onView(withId(R.id.search_edit_text)).perform(typeText("android"))
        // 现在Espresso会自动等待Filter执行完成,再执行断言
        onView(withId(R.id.list_view)).check(matches(hasChildCount(3)))
    }
}

4. 适配现有Filter(无需修改继承关系)

如果项目里已经有很多Filter不想改继承关系,可以用代理模式包装现有Filter:

fun wrapFilter(originalFilter: Filter, idlingResource: FilterIdlingResource): Filter {
    return object : Filter() {
        override fun performFiltering(constraint: CharSequence?): FilterResults {
            idlingResource.incrementActiveFilters()
            return originalFilter.performFiltering(constraint)
        }

        override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
            originalFilter.publishResults(constraint, results)
            idlingResource.decrementActiveFilters()
        }
    }
}

然后在适配器的getFilter()方法里返回包装后的Filter:

override fun getFilter(): Filter {
    return wrapFilter(originalFilterInstance, filterIdlingResource)
}

这样不用修改原有Filter的代码,也能实现全局跟踪。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:11:36