I use the following approach that takes a time zone offset value e.g. GMT-3 and returns the list of time zoneId for the given offset.
private static List<String> getTimeZonesByZoneOffset(final int offset) {
return ZoneId.getAvailableZoneIds()
.stream()
.filter(zoneId -> ZoneId.of(zoneId)
.getRules()
.getOffset(Instant.now())
.equals(ZoneOffset.ofHours(offset)))
.sorted()
.collect(Collectors.toList());
}
Then I retrieve corresponding records that have the same zoneId as my zone list from my database. I use an approach something like this in my service:
public List<Product> getByOffset(int offset) {
final List<String> zones = getTimeZonesByZoneOffset(offset);
final List<Product> products = productRepository.findAllByZone(zones);
return getProductList(products);
}
I want to test these 2 methods in a Unit Test. But I am not sure how should I set the test mechanism. Is there any Unit Test example for that?
There are two static method calls in your method than return different values at different points in time, and I suggest using dependency injection to return mocked results in both those cases.
ZoneRules.getOffset(Instant) returns different values depending if the test is run during Daylight Savings Time or any other timezone transition. You can solve this by testing a fixed time. Add a dependency on Clock and inject it with Clock.systemUTC() in the standard code and Clock.fixed(Instant, ZoneId) for your Unit tests.ZoneId.getAvailableZoneIds() can return more values when more ZoneIds are added. While the available zone IDs are provided by the ZoneRulesProvider abstract class, there is no easy way to disable the standard ZoneIds and inject your own. If you want to solve this by dependency injection then you have to make your own service that returns the available ZoneIds.While the ZoneRules in the ZoneId can change over time (for example, if a governmental body discontinues Daylight Savings Time), they are fixed for times in the past, so it is no problem returning existing ZoneIds as long as Instant.now() is mocked to a time in the past.
Example code:
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class ProductProvider {
private final Clock clock;
private final ZoneIdProvider zoneIdProvider;
public ProductProvider(Clock clock, ZoneIdProvider zoneIdProvider) {
this.clock = clock;
this.zoneIdProvider = zoneIdProvider;
}
private List<String> getTimeZonesByZoneOffset(final int offset) {
return zoneIdProvider.getAvailableZoneIds()
.stream()
.filter(zoneId -> ZoneId.of(zoneId)
.getRules()
.getOffset(clock.instant())
.equals(ZoneOffset.ofHours(offset)))
.sorted()
.collect(Collectors.toList());
}
public List<Product> getByOffset(int offset) {
final List<String> zones = getTimeZonesByZoneOffset(offset);
final List<Product> products = productRepository.findAllByZone(zones);
return getProductList(products);
}
public interface ZoneIdProvider {
Set<String> getAvailableZoneIds();
}
public static class ProductProviderTest {
@Test
public void testTimezone() {
OffsetDateTime testTime = OffsetDateTime.of(2021, 8, 26, 11, 51, 4, 0, ZoneOffset.UTC);
Clock clock = Clock.fixed(testTime.toInstant(), testTime.getOffset());
ZoneIdProvider zoneIdProvider = Mockito.mock(ZoneIdProvider.class);
Mockito.when(zoneIdProvider.getAvailableZoneIds()).thenReturn(Set.of(
ZoneId.of("America/Argentina/Buenos_Aires"), // UTC-3 year-round
ZoneId.of("America/Nuuk"), // UTC-3 as Standard Time only
ZoneId.of("America/Halifax"), // UTC-3 as Daylight Savings Time only
ZoneId.of("Europe/Paris"))); // Never UTC-3
ProductProvider productProvider = new ProductProvider(clock, zoneIdProvider);
Assertions.assertThat(productProvider.getByOffset(-3))
.isEmpty(); // Add your expected products here
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With