
수동 테스트
@Test
void add_menual_test() {
CafeKiosk cafeKiosk = new CafeKiosk();
cafeKiosk.add(new Americano());
System.out.println(">>> 담긴 음료 수 : " + cafeKiosk.getBeverages().size());
System.out.println(">>> 담긴 음료 : " + cafeKiosk.getBeverages().get(0).getName());
}
이 테스트 코드의 문제점
1. 결과를 콘솔에 찍어서 확인하므로 결국 테스트 최종 단계에서 사람이 개입해야 한다.
2. 다른 사람이 이 테스트 코드를 봤을 때 테스트에 대한 명확한 목적을 알 수 없고 테스트의 성공, 실패 여부를 알 수 없다.
다음 단락에서 자동화 테스트에 대해 알아본다.
단위 테스트를 위한 테스트 프레임워크
JUnit5와 AssertJ 이용하여 테스트 코드 작성
프로젝트 build.gradle에 아래 의존성 패키지를 추가해서 사용할 수 있다.testImplementation 'org.springframework.boot:spring-boot-starter-test'
자동화 테스트
@Test
void add() {
CafeKiosk cafeKiosk = new CafeKiosk();
cafeKiosk.add(new Americano());
assertThat(cafeKiosk.getBeverages()).hasSize(1);
assertThat(cafeKiosk.getBeverages().get(0).getName()).isEqualTo("아메리카노");
}
@Test
void remove() {
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
assertThat(cafeKiosk.getBeverages()).hasSize(1);
cafeKiosk.remove(americano);
assertThat(cafeKiosk.getBeverages()).isEmpty();
}
단위 테스트가 많이 작성된다면 프로덕션 로직이 변경 되더라도 지속적인 테스트 코드 수행으로 프로덕션 코드가 정상적으로 동작하고 있는지 체크할 수 있다.
암묵적이거나 아직 드러지나 않은 요구 사항이 있는가? 라는 질문을 항상 던져야한다.
해피 케이스는 사용자가 예상한 대로 시스템이 정확하게 동작하는 이상적인 상황을 테스트한다. 시스템이나 함수가 정상 입력을 받아서 기대한 출력 결과를 내놓는지 확인하는 것이 목적이다.
예외 케이스는 시스템이 비정상적이거나 예외적인 상황에서 어떻게 반응하는지를 테스트한다. 정상 입력 이외의 데이터나 상황으로 인한 오류 처리를 검증하는 것이 목적이다.
해피 케이스와 예외 케이스를 검증할 때는 경계값 테스트가 중요하다.
경계값 : 범위(이상, 이하, 초과, 미만), 구간, 날짜 등
예를 들어, "음료는 최소 3잔 이상부터 주문 가능하다"라는 조건이 있을 때, 해피 케이스는 경계값이 3이므로 3잔 주문에 대해 검증하고, 예외 케이스는 2잔 주문에 대한 검증을 한다.
추가된 요구 사항 : 가게 운영 시간(10:00 ~ 22:00) 외에는 주문을 생성할 수 없다.
public class CafeKiosk {
private static final LocalTime SHOP_OPEN_TIME = LocalTime.of(10, 0);
private static final LocalTime SHOP_CLOSE_TIME = LocalTime.of(22, 0);
public Order createOrder() {
LocalDateTime currentDateTime = LocalDateTime.now();
LocalTime currentTime = currentDateTime.toLocalTime();
if(currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) {
throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
return new Order(currentDateTime, beverages);
}
}
public class CafeKiosk {
private static final LocalTime SHOP_OPEN_TIME = LocalTime.of(10, 0);
private static final LocalTime SHOP_CLOSE_TIME = LocalTime.of(22, 0);
public Order createOrder(LocalDateTime currentDateTime) {
LocalTime currentTime = currentDateTime.toLocalTime();
if(currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) {
throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
return new Order(currentDateTime, beverages);
}
}
테스트할 때는 원하는 시각을 파라미터로 넘기면 된다. 이렇게 변경하면 해피 케이스, 예외 케이스 테스트도 파라미터만 변경하여 경계값 테스트가 가능하다.
@Test
void createOrderOutsideOpenTime() {
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
assertThatThrownBy(() -> cafeKiosk.createOrder(LocalDateTime.of(2024, 11, 6, 9, 59)))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
테스트 하기 어려운 외부로 분리할수록 테스트 가능한 코드는 많아진다.