如何进行单元测试,而不会在使用ZoneId.systemDefault的代码中引发org.threeten.bp.zone.ZoneRulesException异常。

10

我有以下应用程序代码。

应用程序代码

public static long advanceByMax1HourWithoutOverflow(long currentTimeMillis) {
    ZoneId zoneId = ZoneId.systemDefault();

    ZonedDateTime zonedDateTime = Instant.ofEpochMilli(currentTimeMillis).atZone(zoneId);

    // 0-23
    int hour = zonedDateTime.getHour();

    if (hour < 23) {
        zonedDateTime = zonedDateTime.plusHours(1);
        return zonedDateTime.toInstant().toEpochMilli();
    }

    // 0-59
    int minute = zonedDateTime.getMinute();

    if (minute < 59) {
        zonedDateTime = zonedDateTime.plusMinutes(1);
        return zonedDateTime.toInstant().toEpochMilli();
    }

    return currentTimeMillis;
}

我为此编写了一个单元测试,代码如下。

单元测试代码

@Test
public void advanceByMax1HourWithoutOverflow() {
    /*
    27 October 2018
    1540648800000 -> 1540652400000
    22:00 -> 23:00
    */
    long currentTimeMillis = 1540648800000L;
    long expected = 1540652400000L;
    long output = Utils.advanceByMax1HourWithoutOverflow(currentTimeMillis);
    assertEquals(expected, output);

我得到了以下错误。
org.threeten.bp.zone.ZoneRulesException: No time-zone data files registered

我试图通过使用以下代码来修复它:

单元测试代码

@Test
public void advanceByMax1HourWithoutOverflow() {
    Context context = mock(Context.class);
    AndroidThreeTen.init(context);

    /*
    27 October 2018
    1540648800000 -> 1540652400000
    22:00 -> 23:00
    */
    long currentTimeMillis = 1540648800000L;
    long expected = 1540652400000L;
    long output = Utils.advanceByMax1HourWithoutOverflow(currentTimeMillis);
    assertEquals(expected, output);

我正在获取

java.lang.ExceptionInInitializerError
    at org.threeten.bp.ZoneRegion.ofId(ZoneRegion.java:143)
    at org.threeten.bp.ZoneId.of(ZoneId.java:358)
    at org.threeten.bp.ZoneId.of(ZoneId.java:286)
    at org.threeten.bp.ZoneId.systemDefault(ZoneId.java:245)

有没有办法在不改变我的应用程序方法签名的情况下通过单元测试?我希望只保留当前应用程序方法签名而不进行任何修改。
long advanceByMax1HourWithoutOverflow(long currentTimeMillis)

注意,我用于单元测试的Gradle版本是:
implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1'

testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.19.0'
1个回答

18

ThreeTen-Android Backport为Android系统提供了Java SE 8日期时间类的后备,因为它不支持Java 8。根据android开发人员,这个问题的原因是单元测试在JVM上运行,而不是Android:

  

仅在本地机器上运行的单元测试。这些测试被编译为在Java虚拟机(JVM)上本地运行以最小化执行时间。如果您的测试依赖于Android框架中的对象,则建议使用Robolectric。对于依赖于自己的依赖项的测试,请使用模拟对象来模拟您的依赖项的行为。

ThreeTen-Android Backport的所有者Jake Wharton关于Robolectric未能找到资源曾说过:

  

这是一个Robolectric的bug。要么向他们报告,要么切换使用正常的ThreeTenBP进行单元测试。或者两者都用!

因此,我们应该通过使用原始的ThreeTen Backport库来解决这个问题。所以我们应该将其依赖添加到build.gradle文件中,以便由单元测试使用。

在build.gradle文件中:

dependencies {

    implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1'

    testImplementation ('org.threeten:threetenbp:1.3.2'){
        exclude group:'com.jakewharton.threetenabp', module:'threetenabp'
    }
}

在单元测试类中:

@Test
public void advanceByMax1HourWithoutOverflow() {
    /*
    27 October 2018
    1540648800000 -> 1540652400000
    22:00 -> 23:00
    */
    long currentTimeMillis = 1540648800000L;
    long expected = 1540652400000L;
    long output = Utils.advanceByMax1HourWithoutOverflow(currentTimeMillis);
    assertEquals(expected, output);
}

结果:

输入图像描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接