15 Ağustos 2022 Pazartesi

java.time API ile Test

Giriş
4 tane yöntem var. Bunlar şöyle
1. Wrap calls to the java.time API now() methods into a custom static class
2. Use an injectable custom DateTime service.
3. Inject a subclass of java.time.Clock.
4. Intercept static calls to the now() methods with a mocking framework.
1. Static Wrapper yöntemi
Elimizde şöyle bir kod olsun. Bu kod test edebilmek içn setFixed() metodu sağlıyor. Bence çok iyi bir yöntem değil.
public class DateTimeWrapper {
 
  private static LocalDateTime instant; 
  // For testing only
  public static void setFixed(Instant instant) {
    instant = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
  }
 
  public static LocalDateTime currentDateTime() {
    if (instant != null) {
      return instant;
    } else {
      return LocalDateTime.now();
    }
   }
 }
Bu kodun biraz daha gelişmiş hali şöyle
public class DateTimeWrapper {
 
  private static LocalDateTime instant;
  private static Duration offset;
 
  public static void setFixed(Instant instant) {
    DateTimeWrapper.instant = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
    offset = null;
  }
 
  public static void setOffset(Duration duration) {
    offset = duration;
    instant = null;
  }
 
  public static LocalDateTime currentDateTime() {
    if (instant != null) {
      return instant;
    } else if (offset != null) {
      return LocalDateTime.now().plus(offset);
    } else {
      return LocalDateTime.now();
    }
  }
}
2: Custom Mutable DateTimeService Yöntemi

Örnek - Spring
Şöyle yaparız. Burada test için kendi LocalDateTime nesnemizi dönüyoruz.
public interface DateTimeService {
  LocalDateTime currentLocalDateTime();
}

@Profile("!test")
@Service
public class DateTimeServiceImpl implements DateTimeService {
  @Override
  public LocalDateTime currentLocalDateTime(){
    return LocalDateTime.now();
  }
}

// The test implementation contains the same logic as the static wrapper
@Profile("test")
@Service
public class MutableDateTimeService implements DateTimeService {
 
  @Override
  public LocalDateTime currentLocalDateTime() {
    //...
  }
}
Testte şöyle yaparız
@SpringBootTest
@Import(MutableDateTimeService.class)
@ActiveProfiles("test")
public class DateTimeServiceIntegrationTest {

}
3: java.time.Clock Instance Kullanmak
Burada test için kendi Clock nesnemizi dönüyoruz. 
Örnek - Spring
Elimizde şöyle bir kod olsun
@Autowired
Clock clock;

public ExecutionResult runBatchWithClock() {
  LocalDateTime started = LocalDateTime.now(clock);
}
Şöyle yaparız
@Configuration
public class TestConfig {
  @Bean
  Clock fixedClock(){
    return Clock.fixed(Instant.from(someDateTime), ZoneId.systemDefault());}
  }
}
Örnek
Test için elimizde şöyle bir kod olsun
@Slf4j
@Configuration
public class ClockConfig {
  @Value("${time-travel.instant:null}")
  private String timeTravelInstant;
  @Value("${time-travel.zone:null}")
  private String timeTravelZone;
  //@Bean
  //public Clock clock() {
  //  return Clock.system(ZoneOffset.UTC);
  //}
  @Bean
  @ConditionalOnProperty(value = "time-travel.enabled", 
                        havingValue = "false", 
                        matchIfMissing = true)
  public Clock defaultClock() {
    log.info("Using system default zone clock");
    return Clock.systemDefaultZone();
  }
  //@Bean
  //public Clock clock() {
  //  return Clock.fixed(Instant.parse("2022-09-01T12:00:00.00Z"), 
                         ZoneId.of("Europe/Warsaw"));
  //}
  @Bean
  @ConditionalOnProperty(value = "time-travel.enabled", havingValue = "true")
  public Clock clock() {
    log.info("Using fixed clock {} {}", timeTravelInstant, timeTravelZone);
    return Clock.fixed(Instant.parse(timeTravelInstant), ZoneId.of(timeTravelZone));
  }
}
Fixed clock için şöyle yaparız
time-travel.enabled=true
time-travel.instant=2022-09-01T12:00:00.00Z
time-travel.zone=UTC
4. Mockito Kullanmak
Şöyle yaparız. Bence en kolayı bu
@Test
void runMockedDateTimeWithFixedTime() {
  try (MockedStatic<LocalDateTime> mockedStatic=Mockito.mockStatic(LocalDateTime.class)) {
    mockedStatic.when(() -> LocalDateTime.now(ArgumentMatchers.any(Clock.class)))
                .thenReturn(fixedLocalDateTime);

    var result = service.runBatchWithClock();
    assertThat(result.started()).isEqualTo(result.finished());
  }

  // notice that the static mocking is only in effect within the above try block, 
  // which is of course how we would want it.
  assertThat(LocalDateTime.now()
              .getYear())
    .isGreaterThanOrEqualTo(2022);
}




Hiç yorum yok:

Yorum Gönder