- 내가 직접 만들지 않은 코드, 라이브러리, 레거시 시스템에 대한 테스트
- 테스트 대상의 사용방법을 익히고 동작방식을 확인하는데 유용하다.
- 외부 기술, 서비스가 버전이 올라갔을 때 이전과 동일하게 동작하는지 확인할 수도 있다.

학습테스트 예시
- Clock이라는 객체를 바탕으로 학습 테스트의 예시 코드를 확인해보자
@Configuration
public class PaymentConfig {
@Bean
public PaymentService paymentService() {
return new PaymentService(cachedExRateProvider(), clock());
}
@Bean
public ExRateProvider cachedExRateProvider() {
return new CachedExRateProvider(exRateProvider());
}
@Bean
public ExRateProvider exRateProvider() {
return new WebApiExRateProvider();
}
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}
public class PaymentService {
final ExRateProvider exRateProvider;
final Clock clock;
public PaymentService(ExRateProvider exRateProvider, Clock clock) {
this.exRateProvider = exRateProvider;
this.clock = clock;
}
public Payment prepare(Long orderId, String currency, BigDecimal foreignCurrencyAmount) throws IOException {
BigDecimal exRate = exRateProvider.getExRate(currency);
BigDecimal convertedAmount = foreignCurrencyAmount.multiply(exRate);
LocalDateTime validUntil = LocalDateTime.now(clock).plusMinutes(30);
return new Payment(orderId, currency, foreignCurrencyAmount, exRate, convertedAmount, validUntil);
}
}
- PaymentConfig에 Clock을 정의하고
Clock.systemDefaultZone() 를 반환하게 한다.
- 그리고 이를 PaymentService의 생성자로 주입한다.
- 서비스 객체에선 이제 정의한 Clock을 시간 객체 담아 적용한다.
LocalDateTime.*now*(clock) 서비스 로직은 크게 달라질 부분이 없다. Clock은 서버 시간에 맞춰 변하는 값으로 정의했기 때문이다.
@Configuration
public class TestPaymentConfig {
@Bean
public PaymentService paymentService() { return new PaymentService(exRateProvider(), clock()); }
@Bean
public ExRateProvider exRateProvider() { return new ExRateProviderStub(BigDecimal.valueOf(1_000)); }
@Bean
public Clock clock() {
return Clock.fixed(Instant.now(), ZoneId.systemDefault());
}
}
class PaymentServiceTest {
Clock clock;
@BeforeEach
void beforeEach() {
this.clock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
}
@Test
void convertedAmount() throws IOException {
testAmount(BigDecimal.valueOf(500), BigDecimal.valueOf(5_000), clock);
testAmount(BigDecimal.valueOf(1000), BigDecimal.valueOf(10_000), clock);
testAmount(BigDecimal.valueOf(3000), BigDecimal.valueOf(30_000), clock);
}
@Test
void validUntil() throws IOException {
PaymentService paymentService = new PaymentService(new ExRateProviderStub(BigDecimal.valueOf(1_000)), clock);
Payment payment = paymentService.prepare(1L, "USD", BigDecimal.TEN);
// valid until이 prepare()의 30분 뒤로 설정됐는가?
LocalDateTime now = LocalDateTime.now(clock);
LocalDateTime expectedValidUntil = now.plusMinutes(30);
Assertions.assertThat(payment.getValidUntil()).isEqualTo(expectedValidUntil);
}
private static void testAmount(BigDecimal exRate, BigDecimal convertedAmount, Clock clock) throws IOException {
// given
PaymentService paymentService = new PaymentService(new ExRateProviderStub(exRate), clock);
// when
Payment payment = paymentService.prepare(1L, "USD", BigDecimal.TEN);
// then
Assertions.assertThat(payment.getExRate()).isEqualByComparingTo(exRate);
Assertions.assertThat(payment.getConvertedAmount()).isEqualByComparingTo(convertedAmount);
}
}
- 테스트를 위해, TestPaymentConfig에도 Clock을 정의한다.
Clock.fixed(Instant.now(), ZoneId.systemDefault());
- Clock.fixed를 사용하여 항상 같은 시간을 반환하도록 정의한다.
new PaymentService(new ExRateProviderStub(BigDecimal.valueOf(1_000)), clock); 이런 식으로 PaymentService 객체를 만들 때 TestPaymentConfig에 정의된 fixed Clock이 전달되게 된다.
- 그리고 paymentService.prepare가 반환하는 payment에는 validUntil이라는 값이 담기는데, 우리는PaymentService에서 이를 현재시간 + 30분으로 정의해두었다.
- 이제 테스트 코드에서 새롭게 LocalDateTime.now(clock)로 정의하더라도 paymentService를 통해 얻었떤 payment의 LocalDateTime.now(clock)시간과 완전히 동일한 시간을 만들게 된다.