Android Studio中用JUnit和Mockito测试android.icu.text相关Java类的问题
问题分析
Android单元测试环境中,android.icu.text下的类属于Android框架API,默认JUnit单元测试环境无法真实实例化这些类。开启unitTests.returnDefaultValues = true后,框架会返回默认值(如null),导致DecimalFormat.format()返回null,测试失败。
解决方案
方案1:用Mockito模拟依赖(推荐)
通过Mockito模拟DecimalFormat和DecimalFormatSymbols的行为,避免直接依赖Android框架类。先添加Mockito依赖到build.gradle:
dependencies { testImplementation 'org.mockito:mockito-core:4.11.0' testImplementation 'junit:junit:4.13.2' }
修改测试类,模拟方法逻辑:
import android.icu.text.DecimalFormat; import android.icu.text.DecimalFormatSymbols; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class NumberFormatterTest { @Test public void format_givenInputStringAndPatternString_shouldReturnCorrespondingFormattedString(){ String pattern = "#,###.##"; String inputString = "1000.12"; String expect = "1,000.12"; NumberFormatter formatter = Mockito.spy(new NumberFormatter()); DecimalFormat mockFormat = Mockito.mock(DecimalFormat.class); Mockito.when(mockFormat.format(1000.12d)).thenReturn(expect); Mockito.doReturn(mockFormat).when(formatter).getDecimalFormat(Mockito.anyString(), Mockito.any(DecimalFormatSymbols.class)); String actual = formatter.format(inputString, pattern); Assert.assertEquals(expect, actual); } }
方案2:使用Robolectric测试
Robolectric可以模拟完整Android运行环境,支持android.icu类的真实行为。添加Robolectric依赖:
dependencies { testImplementation 'org.robolectric:robolectric:4.12.1' testImplementation 'junit:junit:4.13.2' }
修改测试类,使用RobolectricRunner:
import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class NumberFormatterTest { @Test public void format_givenInputStringAndPatternString_shouldReturnCorrespondingFormattedString(){ String pattern = "#,###.##"; String inputString = "1000.12"; String expect = "1,000.12"; String actual = new NumberFormatter().format(inputString, pattern); Assert.assertEquals(expect, actual); } }
方案3:重构代码,依赖抽象
将格式化逻辑抽象成接口,测试时用Java标准库实现解耦:
1. 定义抽象接口
public interface NumberFormatStrategy { String format(double value, String pattern); }
2. 实现Android端策略
import android.icu.text.DecimalFormat; import android.icu.text.DecimalFormatSymbols; import java.util.Locale; public class AndroidIcuNumberFormatStrategy implements NumberFormatStrategy { @Override public String format(double value, String pattern) { DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault()); symbols.setDecimalSeparator('.'); symbols.setGroupingSeparator(','); return new DecimalFormat(pattern, symbols).format(value); } }
3. 修改NumberFormatter类
import androidx.annotation.NonNull; public class NumberFormatter { private final NumberFormatStrategy formatStrategy; public NumberFormatter() { this(new AndroidIcuNumberFormatStrategy()); } public NumberFormatter(@NonNull NumberFormatStrategy formatStrategy) { this.formatStrategy = formatStrategy; } public String format(String inputText, String pattern){ double value = Double.parseDouble(inputText); return formatStrategy.format(value, pattern); } }
4. 测试用Java标准库实现
import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Locale; import org.junit.Assert; import org.junit.Test; public class NumberFormatterTest { @Test public void format_givenInputStringAndPatternString_shouldReturnCorrespondingFormattedString(){ String pattern = "#,###.##"; String inputString = "1000.12"; String expect = "1,000.12"; NumberFormatStrategy testStrategy = (value, pat) -> { DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault()); symbols.setDecimalSeparator('.'); symbols.setGroupingSeparator(','); return new DecimalFormat(pat, symbols).format(value); }; String actual = new NumberFormatter(testStrategy).format(inputString, pattern); Assert.assertEquals(expect, actual); } }
关键说明
unitTests.returnDefaultValues = true仅能避免测试崩溃,不会提供真实方法实现,因此会返回null。- Mockito适合隔离测试业务逻辑,Robolectric适合模拟真实Android环境,重构抽象是最彻底的解耦方案。
内容的提问来源于stack exchange,提问作者Trần Võ Bảo
相关产品推荐
相关产品推荐

