Android Studio(Kotlin)中Mock Menu与MenuItem进行单元测试的方法
你提供的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
相关产品推荐
相关产品推荐

