Как установить часовой пояс для SimpleDateFormat в модульном тесте Android - PullRequest
0 голосов
/ 16 декабря 2018

Я хочу протестировать метод в служебном классе Date, который получает Date из String.Переданная строка - 1980-03-26T00:00:00.000+0200, и я хочу сравнить полученную дату с assertEquals.Тест не пройден с таким выводом:

org.junit.ComparisonFailure: 
Expected :Wed Mar 26 00:00:00 PST 1980
Actual   :Wed Mar 26 00:00:00 SGT 1980

Вот мой тест:

INITIAL_DATA_DATE_FROM_STRING = "1980-03-26T00:00:00.000+0200";
EXPECTED_DATA_DATE_FROM_STRING = "Wed Mar 26 00:00:00 PST 1980";

// inside the method ...

date = DateUtils.getDateFromString(INITIAL_DATA_DATE_FROM_STRING);
assertEquals(EXPECTED_DATA_DATE_FROM_STRING, String.valueOf(date));

Вот метод, который я тестирую:

    public static Date getDateFromString(String dateAsString) {
        return getDateFromString(dateAsString, "dd/MM/yyyy");
    }

    public static Date getDateFromString(String dateAsString, String dateFormat) {
        Date formattedDate = null;
        if (StringUtils.notNull(dateAsString)) {
            DateFormat format = new SimpleDateFormat(dateFormat);
            try {
                formattedDate = parseString(dateAsString, format);
            } catch (ParseException e) {
                try {
                    formattedDate = parseString(dateAsString, new SimpleDateFormat("yyyy-MM-dd"));
                } catch (ParseException e1) {
                    // handle exception code
                }
            }
        }
        return formattedDate;
    }

Итак, модульный тестне зависит от часового пояса.Есть ли способ установить часовой пояс по умолчанию только для модульного тестирования?

1 Ответ

0 голосов
/ 17 декабря 2018

java.time или Joda-Time

Классы Date и SimpleDateFormat имеют проблемы с дизайном, поэтому не используйте их.Первая версия вашего вопроса была помечена jodatime, и если вы используете Joda-Time в своем проекте, это уже значительное улучшение.Joda-Time предоставляет вам всю необходимую функциональность, поэтому ваш метод, вероятно, должен возвращать тип запроса от Joda-Time, а не старомодный Date.

Joda-Time также находится на пути к выходу на пенсию.Однако его преемником является java.time, современный Java-интерфейс даты и времени.Таким образом, последний вариант вы можете рассмотреть независимо от того, используете ли вы Joda-Time или нет.Поскольку ваша строка содержит смещение UTC, один из вариантов - вернуть OffsetDateTime.Чтобы проверить метод, который делает это:

    assertEquals(OffsetDateTime.of(1980, 3, 26, 0, 0, 0, 0, ZoneOffset.ofHours(2)),
            DateUtils.getOffsetDateTimeFromString("1980-03-26T00:00:00.000+0200"));

Ваш пример и ваш код могут создать впечатление, что вы только после даты из строки и не заботитесь ни о времени дня, ни о смещении.Если это правильно, ваш метод может вернуть LocalDate и быть протестирован следующим образом:

    assertEquals(LocalDate.of(1980, Month.MARCH, 26),
            DateUtils.getLocalDateFromString("1980-03-26T00:00:00.000+0200"));

Последний также освободит вас от всех соображений, связанных с часовым поясом.В обоих случаях обратите внимание, что я передаю объекты даты и времени в assertEquals, а не в строки.

Ваш модульный тест правильный: ваш метод возвращает неправильное время

Ошибка, о которой вы сообщилив вашем вопросе было то, что хотя ожидаемый Date от вашего метода был Wed Mar 26 00:00:00 PST 1980, фактическое значение было Wed Mar 26 00:00:00 SGT 1980.Это правильно, что они отличаются.Полночь в Нижней Калифорнии, Юконе, Вашингтоне, Неваде, Калифорнии и других местах, где наблюдается тихоокеанское время, отличается от полуночи в Китае.

Можно ли использовать java.time на Android?

Да, java.time прекрасно работает на старых и новых устройствах Android.Для этого требуется как минимум Java 6 .

  • В Java 8 и более поздних версиях и на более новых устройствах Android (от уровня API 26) современный API поставляется встроенным.
  • В Java 6 и 7 получают ThreeTen Backport, бэкпорт новых классов (ThreeTen для JSR 310; см. Ссылки внизу).
  • На (более старых) Android используется версия ThreeTen для AndroidBackport.Это называется ThreeTenABP.И убедитесь, что вы импортируете классы даты и времени из org.threeten.bp с подпакетами.

Если вы не хотите, чтобы ваш код Android зависел от сторонней библиотеки, вы все равно можете использовать java.time в вашем модульном тесте.Чтобы протестировать метод, который возвращает старомодный Date:

    Instant expectedInstant = LocalDate.of(1980, Month.MARCH, 26)
            .atStartOfDay(ZoneId.systemDefault())
            .toInstant();
    Date expectedDate = Date.from(expectedInstant);
    Date actualDate = DateUtils.getDateFromString("1980-03-26T00:00:00.000+0200");
    assertEquals(expectedDate, actualDate);

При использовании обратного порта (ThreeTen Backport и / или ThreeTenABP) преобразование из Instant ot Date происходит немного по-другому:

    Date expectedDate = DateTimeUtil.toDate(expectedInstant);

Снова обратите внимание, что я сравниваю Date объекты, а не строки.

Как установить часовой пояс для SimpleDateFormat?

Чтобы ответить на вопрос вВаш заголовок: используйте метод setTimeZone:

    DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXX");
    df.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); // Pacific Time
    System.out.println(df.parseObject("1980-03-26T00:00:00.000+0200"));

В этом конкретном случае это не будет иметь никакого значения, поскольку смещение в строке анализируется и имеет приоритет над часовым поясомformatter.

Можно ли установить часовой пояс по умолчанию только для модульного тестирования?

Есть несколько вариантов.

    TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));

Поместить это в beforeClass или before метод вашего модульного теста установит часовой пояс вашей JVM в Pacific Time.Это не пуленепробиваемое, так как другой код может установить его на что-то другое до завершения теста, но вы можете контролировать, что этого не происходит.Обычно я бы не рекомендовал использовать устаревший класс TimeZone.У него тоже есть проблемы с дизайном, но это естественный устаревший выбор, если тестируемые вами методы используют устаревший SimpleDateFormat.Одна из проблем заключается в том, что она не сообщает, является ли переданная строка недействительной.Чтобы получить правильную проверку, просто пройдите современный класс:

    TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("America/Los_Angeles")));

Или используйте backport:

    TimeZone.setDefault(DateTimeUtils.toTimeZone(ZoneId.of("America/Los_Angeles")));

Ссылки

...