Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test code which makes use of ZoneId.systemDefault without raising org.threeten.bp.zone.ZoneRulesException

I have the following application code.

Application code

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;
}

I wrote a unit test for it with the following.

Unit test code

@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);

I got the following error

org.threeten.bp.zone.ZoneRulesException: No time-zone data files registered

I tried to fix it by using

Unit test code

@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);

I'm getting

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)

Is there any way I can pass the unit test, without changing my current app's method signature? I would like to just retain my current app method signature without any modification.

long advanceByMax1HourWithoutOverflow(long currentTimeMillis)

Note, the gradle which I use for unit testing are

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

testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.19.0'
like image 634
Cheok Yan Cheng Avatar asked Oct 27 '18 20:10

Cheok Yan Cheng


People also ask

What is ZoneId systemDefault ()?

The systemDefault() method of the ZoneId class in Java is used to return the system default time-zone. Syntax: public String systemDefault() Parameters: This method does not accepts any parameters. Return Value: This method returns the zone ID.


1 Answers

ThreeTen-Android Backport provides a backport of the Java SE 8 date-time classes to Android system which does not support Java 8. The cause of this issue is that the unit test runs on JVM, not Android according to android developer:

Unit tests that run on your local machine only. These tests are compiled to run locally on the Java Virtual Machine (JVM) to minimize execution time. If your tests depend on objects in the Android framework, we recommend using Robolectric. For tests that depend on your own dependencies, use mock objects to emulate your dependencies' behavior.

Jake Wharton (ThreeTen-Android Backport owner) about not founding resources by Robolectric has said:

This is a Robolectric bug. Either report to them or switch to use the normal ThreeTenBP in your unit tests. Or both!

As a result we should solve this problem by using original ThreeTen Backport library. So we should add its dependency to the build.gradle file to be used by unit test.

In build.gradle file:

dependencies {

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

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

In unit test class:

@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);
}

Result:

enter image description here

like image 51
aminography Avatar answered Oct 28 '22 19:10

aminography