如何用Mockito模拟并验证Android ICU的NumberFormatter
问题解决方案
方案A:解决Measure.equals未mock的异常
问题根源:Android单元测试环境中,android.icu.util.Measure属于未被完全实现的SDK类,直接在verify中比较Measure对象会触发未mock的equals方法,导致异常。可以通过以下两种方式解决:
方式1:用ArgumentCaptor捕获参数验证属性
@Test fun testA() { val formattedNumber = mock<FormattedNumber> { on { toString() } doReturn "X units" } val numberFormatter = mock<LocalizedNumberFormatter> { on { unitWidth(any()) } doReturn it on { format(any<Measure>()) } doReturn formattedNumber } val formatter = CustomFormatter(numberFormatter) formatter.formatA("stuff") // 捕获传入format方法的Measure参数 val measureCaptor = ArgumentCaptor.forClass(Measure::class.java) verify(numberFormatter).format(measureCaptor.capture()) // 验证捕获到的Measure的数值和单位是否正确 val capturedMeasure = measureCaptor.value assertEquals(10L, capturedMeasure.number) assertEquals(MeasureUnit.SECOND, capturedMeasure.unit) }
方式2:自定义ArgumentMatcher匹配参数
@Test fun testA() { val formattedNumber = mock<FormattedNumber> { on { toString() } doReturn "X units" } val numberFormatter = mock<LocalizedNumberFormatter> { on { unitWidth(any()) } doReturn it on { format(any<Measure>()) } doReturn formattedNumber } val formatter = CustomFormatter(numberFormatter) formatter.formatA("stuff") // 自定义匹配逻辑,检查Measure的内部属性 verify(numberFormatter).format(argThat { it.number == 10L && it.unit == MeasureUnit.SECOND }) }
方案B:解决NullPointerException问题
NPE的核心原因是链式调用的mock返回错误:LocalizedNumberFormatter的unitWidth、unit方法实际返回的是构建器实例(而非自身),但你的mock让这些方法返回了原LocalizedNumberFormatter对象,导致后续方法调用出现类型不兼容或null。
正确的mock方式需要拆分链式调用的每个节点:
@Test fun testB() { val formattedNumber = mock<FormattedNumber> { on { toString() } doReturn "X units" } // mock设置unit后的构建器,负责处理后续format调用 val unitBuilder = mock<LocalizedNumberFormatter> { on { unit(any<MeasureUnit>()) } doReturn it on { format(any<Long>()) } doReturn formattedNumber } // 初始mock,unitWidth返回设置unit的构建器 val numberFormatter = mock<LocalizedNumberFormatter> { on { unitWidth(any<NumberFormatter.UnitWidth>()) } doReturn unitBuilder } val formatter = CustomFormatter(numberFormatter) formatter.formatB("stuff") verify(numberFormatter).unitWidth(NumberFormatter.UnitWidth.FULL_NAME) verify(unitBuilder).unit(MeasureUnit.SECOND) verify(unitBuilder).format(10L) }
额外注意事项
- Android单元测试中,ICU相关类属于SDK stub类,没有实际实现,避免直接比较对象,优先验证内部属性
- Mock链式调用时,必须严格遵循原API的返回类型,每个方法返回对应的后续构建器实例,不能一概返回原mock对象
内容的提问来源于stack exchange,提问作者MrKew
相关产品推荐
相关产品推荐

