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

Android Studio(Kotlin)中Mock Menu与MenuItem进行单元测试的方法

如何Mock Menu和MenuItem来测试MenuUtil工具类?

你提供的MenuUtil工具类代码如下:

object MenuUtil{

    fun updateMenuIconTint(menu: Menu, @ColorInt color: Int) {
        menu.forEach{
            it.updateTint(color)
        }  
    }
    fun MenuItem.updateTint(@ColorInt color: Int){
        var icDrawable = DrawableCompat.wrap(icon)
        icDrawable = icDrawable.mutate()
        DrawableCompat.setTintList(icDrawable, ColorStateList.valueOf(color))
    }

}

由于Menu和MenuItem都是Android系统的接口,无法直接实例化,我们可以用Mockito框架Mock这些接口对象来完成单元测试,具体步骤如下:

1. 添加测试依赖

首先确保项目中引入Mockito相关依赖(以Gradle为例):

testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.6.0'
testImplementation 'org.mockito.kotlin:mockito-kotlin:5.1.0' // 可选,简化Kotlin语法的封装

2. 测试updateMenuIconTint方法

该方法核心是遍历Menu的所有Item并调用updateTint,我们需要Mock Menu和多个MenuItem,验证遍历逻辑与方法调用:

import android.view.Menu
import android.view.MenuItem
import android.graphics.Color
import org.junit.Test
import org.mockito.Mockito.*

class MenuUtilTest {

    @Test
    fun updateMenuIconTint_遍历所有MenuItem并调用updateTint() {
        // 1. Mock Menu和两个MenuItem实例
        val mockMenu: Menu = mock(Menu::class.java)
        val mockItem1: MenuItem = mock(MenuItem::class.java)
        val mockItem2: MenuItem = mock(MenuItem::class.java)
        
        // 2. 让Menu的iterator返回包含两个mock Item的迭代器(Kotlin forEach依赖iterator)
        `when`(mockMenu.iterator()).thenReturn(listOf(mockItem1, mockItem2).iterator())
        
        // 3. 执行测试方法
        val testColor = Color.RED
        MenuUtil.updateMenuIconTint(mockMenu, testColor)
        
        // 4. 验证每个MenuItem都被调用了updateTint,且参数正确
        verify(mockItem1).updateTint(testColor)
        verify(mockItem2).updateTint(testColor)
    }
}

3. 测试updateTint扩展方法

该方法涉及DrawableCompat的静态方法调用,需要Mock静态方法来验证逻辑:

import android.view.MenuItem
import android.graphics.drawable.Drawable
import android.graphics.Color
import androidx.core.graphics.drawable.DrawableCompat
import org.junit.Test
import org.mockito.Mockito.*

class MenuUtilTest {

    // ... 上面的测试方法 ...

    @Test
    fun updateTint_为MenuItem图标设置正确的颜色tint() {
        // 1. Mock MenuItem和Drawable实例
        val mockItem: MenuItem = mock(MenuItem::class.java)
        val originalDrawable: Drawable = mock(Drawable::class.java)
        val wrappedDrawable: Drawable = mock(Drawable::class.java)
        
        // 2. 模拟MenuItem的icon返回mock Drawable
        `when`(mockItem.icon).thenReturn(originalDrawable)
        // 模拟mutate返回自身
        `when`(wrappedDrawable.mutate()).thenReturn(wrappedDrawable)
        
        // 3. Mock DrawableCompat的静态方法
        mockStatic(DrawableCompat::class.java).use { mockedCompat ->
            // 模拟wrap方法返回我们mock的wrappedDrawable
            mockedCompat.`when`<Drawable> { DrawableCompat.wrap(originalDrawable) }.thenReturn(wrappedDrawable)
            
            // 4. 执行测试方法
            val testColor = Color.BLUE
            mockItem.updateTint(testColor)
            
            // 5. 验证setTintList被正确调用,参数为目标颜色的ColorStateList
            mockedCompat.verify {
                DrawableCompat.setTintList(wrappedDrawable, ColorStateList.valueOf(testColor))
            }
        }
    }
}

关键注意点

  • Menu是接口,Mockito可直接通过mock(Menu::class.java)生成Mock实例
  • Kotlin对Menu的forEach扩展函数依赖iterator()方法,因此需要Mockmenu.iterator()返回包含测试用MenuItem的迭代器
  • 对于DrawableCompat这类静态工具类,需用Mockito的mockStatic方法模拟静态方法行为(要求Mockito 3.4.0+版本)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:55:19