如何为含自定义属性的自定义视图编写单元测试?
我之前在测试自定义View获取自定义属性时也踩过类似的坑,给你几个靠谱的解决方案,应该能帮你搞定!
方法1:用Robolectric模拟Android环境(推荐)
Robolectric可以提供接近真实的Android Context实现,不用自己手动Mock一堆依赖,能大大降低测试复杂度。
步骤:
- 首先在你的
build.gradle里添加Robolectric的依赖(确保版本和你的AndroidX版本兼容) - 编写测试类时,用
@RunWith(RobolectricTestRunner.class)标注 - 直接用
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设置对应属性的返回值,别忘了Mock
recycle()方法避免泄漏警告
示例代码:
@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
相关产品推荐
相关产品推荐

