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

如何为含自定义属性的自定义视图编写单元测试?

我之前在测试自定义View获取自定义属性时也踩过类似的坑,给你几个靠谱的解决方案,应该能帮你搞定!

方法1:用Robolectric模拟Android环境(推荐)

Robolectric可以提供接近真实的Android Context实现,不用自己手动Mock一堆依赖,能大大降低测试复杂度。

步骤:

  1. 首先在你的build.gradle里添加Robolectric的依赖(确保版本和你的AndroidX版本兼容)
  2. 编写测试类时,用@RunWith(RobolectricTestRunner.class)标注
  3. 直接用ApplicationProvider.getApplicationContext()获取可用的Context,再模拟AttributeSet来传递自定义属性

示例代码:

@RunWith(RobolectricTestRunner.class)
public class CustomViewTest {

    @Test
    public void testCustomAttributeIsLoadedCorrectly() {
        // 模拟XML中的自定义属性
        AttributeSet attrs = Robolectric.buildAttributeSet()
                .addAttribute(R.attr.your_custom_attr, "100") // 这里填你属性的预期值
                .build();

        // 实例化自定义View
        YourCustomView customView = new YourCustomView(ApplicationProvider.getApplicationContext(), attrs);

        // 验证View是否正确获取了属性值
        assertEquals(100, customView.getYourCustomAttrValue());
    }
}

如果你的View是从XML布局加载的,还可以直接用LayoutInflater加载布局,更贴近真实场景:

@Test
public void testCustomAttributeFromLayout() {
    View view = LayoutInflater.from(ApplicationProvider.getApplicationContext())
            .inflate(R.layout.your_custom_view_layout, null);
    YourCustomView customView = (YourCustomView) view;
    
    assertEquals(100, customView.getYourCustomAttrValue());
}

方法2:纯Mockito手动Mock Context与TypedArray

如果不想引入Robolectric,纯Mockito也能搞定,但要注意不能只Mock Context本身,必须把Context关联的Resources和Theme也Mock好,否则View初始化时会因为找不到Theme而判定Context为null。

关键要点:

  • Mock Context的getResources()和getTheme()方法,返回Mock的Resources和Theme
  • 让Mock的Theme在调用obtainStyledAttributes()时返回你预先Mock好的TypedArray
  • 给TypedArray设置对应属性的返回值,别忘了Mockrecycle()方法避免泄漏警告

示例代码:

@RunWith(MockitoJUnitRunner.class)
public class CustomViewTest {

    @Mock
    private Context mockContext;
    @Mock
    private Resources mockResources;
    @Mock
    private Theme mockTheme;
    @Mock
    private TypedArray mockTypedArray;

    @Before
    public void setup() {
        // 关联Context的Resources和Theme
        when(mockContext.getResources()).thenReturn(mockResources);
        when(mockContext.getTheme()).thenReturn(mockTheme);
        
        // 让Theme返回我们Mock的TypedArray
        when(mockTheme.obtainStyledAttributes(any(int[].class)))
                .thenReturn(mockTypedArray);
        
        // 设置自定义属性的预期返回值
        when(mockTypedArray.getInt(R.attr.your_custom_attr, 0))
                .thenReturn(100);
        
        // Mock recycle()方法,避免测试时抛出资源泄漏警告
        doNothing().when(mockTypedArray).recycle();
    }

    @Test
    public void testCustomAttribute() {
        YourCustomView customView = new YourCustomView(mockContext);
        assertEquals(100, customView.getYourCustomAttrValue());
    }
}

常见误区提醒

  • 不要直接Mock Context却不处理getTheme():View初始化时会调用context.getTheme()来获取TypedArray,如果这个方法返回null,就会抛出你遇到的“Context为null”相关异常
  • 别忘了回收TypedArray:即使是Mock的TypedArray,View里的代码一般都会调用recycle(),所以要Mock这个方法避免测试报错
  • 确保自定义View的构造方法正确处理AttributeSet:比如在多参数构造方法里调用obtainStyledAttributes,并且把属性值赋值给成员变量

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:55:45