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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:11:29