Java, TDD Правда когда не равняется? - PullRequest
0 голосов
/ 14 октября 2019

по какой-то причине во время тестирования этого метода во время урока в классе мы обнаружили проблему, которую не могли понять. При записи System.out.println(); почему-то это проходит ?. Может кто-нибудь объяснить, почему это происходит?

public class Zones {

public ZoneId getZoneId(String input) {

    if (input.equalsIgnoreCase("Stockholm")) {
        return ZoneId.of("Europe/Stockholm");
    }
    else if (input.equalsIgnoreCase("Shanghai")) {
        return ZoneId.of("Asia/Shanghai");
    } else if (input.equalsIgnoreCase("Toronto")) {
        return ZoneId.of("America/Toronto");
    }
    else if (input.equalsIgnoreCase("Hamburg")) {
        return ZoneId.of("Europe/Berlin");
    }
    else return null;
}

public LocalDateTime getZoneTime(ZoneId zoneId) {
    LocalDateTime lt = LocalDateTime.now(zoneId);
    return lt;
}

}

private Zones z = new Zones();

@Test
public void getZoneTimeTest () {
    System.out.println(z.getZoneTime(zIDToronto).getNano() );
    System.out.println(LocalDateTime.now(zIDToronto).getNano() );
    assertTrue(z.getZoneTime(zIDToronto).getNano() == LocalDateTime.now(zIDToronto).getNano());
}

Ответы [ 2 ]

1 голос
/ 17 октября 2019

Наконец-то появилось время для более глубокого изучения.

Я начал экспериментировать и через некоторое время обнаружил, что на самом деле не присутствие System.out.println повлияло на результат, а тот факт, что вы создаете экземпляр 2LocalDateTime экземпляров перед ним.

Копая глубже, в код LocalDateTime и SystemClock (которому он делегирует), я обнаружил, что точность до миллимиллиона была достигнута путем вызовасобственный вызов jdk.internal.misc.VM#getNanoTimeAdjustment.

Последний вызов зависит от ОС. Я немного поэкспериментировал с этим и обнаружил, что он не возвращает значения линейно, так как он вызывается в цикле (при условии, что мой цикл выполняется довольно регулярно).

Поэтому я решил запустить некоторый код для сопоставления возвращаемого значенияnano values.

Я сделал этот код выборки:

Clock clock = Clock.systemDefaultZone();

int samples = 1_000;
LocalDateTime[] instants = new LocalDateTime[samples];

int k = 0;

for (int i = 0; i < samples; i++) {
    instants[i] = LocalDateTime.now(clock);
    for (int j = 0; j < 10000; j++) {
        k = j % 2;
    }
}

записал значения в файл, а затем отобразил наноразности относительно первого значения в графике:

nano progression vs first invocation

Как вы можете видеть, этот график (из 1000 значений) делает прерывистые скачки. Это, очевидно, частично из-за ограничения точности базовой системы. Но Ват меня поразил тем, что первые два значения последовательно были разными. Это похоже на то, как будто при обычном доступе система начинает некоторое время кэшировать значение (возможно, чтобы избежать нагрузки на системные ресурсы).

Но в результате получается, что вы настроили себя на получение того же значения на3-й и 4-й вызов (если не прошло достаточно времени).

Это объяснило бы, почему ваш тест проходит с неудачными тестами и завершается неудачей.

В качестве отступления, для модульных тестов, которые вы надеваетене хочу полагаться на системные часы. Убедитесь, что ваш бизнес-код получает время от внедренного экземпляра Clock. Затем вы можете ввести собственные часы для тестов и проверить, будет ли ваш код работать в день перехода на летнее время или в високосный день, не дожидаясь нескольких месяцев.

0 голосов
/ 15 октября 2019

Тест включает в себя состояние гонки и проходит (иногда) из-за того, что время и добавление операторов изменяют время и, следовательно, результат теста.

Условие, которое проверяется в утверждении, в основном таково:равенство наносекундных частей двух дат-таймов, взятых одна за другой подряд.

Учитывая, что по умолчанию System.currentTimeMillis() используется внутри LocalDateTime.now и имеет точность не более миллисекунды, проверка будет успешной, есливторая последовательность вызовов для получения количества наносекунд достаточно быстрая (то есть фактическая последовательность вызовов, приводящая к вызову System.currentTimeMillis, завершенному в течение той же миллисекунды, что и первая).

Когда вы вызываете функциииспользуется для получения значения наносекунды перед фактическим утверждением, загружаются соответствующие классы, код соответствующих методов попадает в кеши ЦП и т. д. Это делает вторую пару вызовов для получения количества наносекунд намного быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...