목차
1. 테스트 코드를 작성해야 하는 이유
2. JUnit5 란?
3. Assertj
4. 수동테스트 VS 자동화된 케이스
5. 테스트 케이스 세분화 하기
테스트 코드란?
- 테스트 코드는 소프트웨어의 기능과 동작을 테스트하는데 사용되는 코드이다.
- 테스트 코드는 개발자가 작성한 코드를 실행하고 예상된 결과가 나오는지 확인하는데 사용된다.
1. 테스트 코드를 작성해야 하는 이유
- 코드의 품질 향상
- 테스트 코드를 통해 발생 가능성 있는 버그를 사전에 찾아내고 방지할 수 있으며, 이는 개발자가 신뢰할 수 있는 코드를 작성할 수 있게 도와준다.
- 문서화
- 테스트 코드는 개발자가 기능의 동작 방식을 이해하는데 도움이 되는 문서로 작용할 수 있다.
- 테스트 코드를 통해 코드의 예상 동작을 명확하게 확인할 수 있으며, 개발자 간의 커뮤니케이션 향상에도 도움이 된다.
- 리팩토링
- 테스트 코드가 있는 경우 코드를 리팩토링할때 기존 기능이 여전히 올바르게 작동하는지 확인할 수 있다.
- 변경 사항이 예상치 못한 부작용을 일으키지 않도록 하며, 코드의 동작이 바뀌지 않았는지 확인할 수 있다.
테스트 종류
단위 테스트(Unit Test)
단위 테스트는 응용 프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 예상대로 동작하는지 확인하는 테스트이다. 테스트 대상 단위의 크기는 엄격하게 정해져 있지 않지만 일반적으로 클래스 또는 메서드 수준으로 정해진다.
통합 테스트(Integreation Test)
통합 테스트는 단위 테스트보다 더 큰 동작을 달성하기 위해 여러 모듈들을 모아 이들이 의도대로 협력하는지 확인하는 테스트이다. 단위 테스트와 달리 개발자가 변경할 수 없는 부분(외부 라이브러리)까지 묶어 검증할 때 사용한다. 이는 DB에 접근하거나 전체 코드와 다양한 환경이 제대로 작동하는지 확인하는데 필요한 모든 작업을 수행할 수 있다.
2. JUnit5 란?
JUnit5 = JUnit Platform + JUnit Jupiter + Junit Vintage
JUnit5는 자바 프로그래밍 언어를 위한 테스트 프레임워크이다. JUnit은 소프트웨어의 단위 테스트를 작성하고 실행하는 데 사용되는 도구로서, 코드의 품질을 검증하고 버그를 감지하기 위해 개발자들에게 도움을 주는 역할을 한다. JUnit5는 이전 버전인 JUnit4의 다음 세데 버전으로 개발되었다. JUnit5는 자바 8 이상부터 사용 가능하다.
spring boot 에서 JUnit5 사용하기
build.gradle
//test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Test Class 생성: 단축키 Crtl + Shift + T
3. AssertJ
AssertJ는 자바 프로그래밍 언어를 위한 테스트 단언 라이브러리이다. assertion은 코드의 동작을 검증하고 예상한 결과와 실제 결과가 일치하는지를 확인하는데 사용한다.
AssertJ는 테스트 코드를 더 읽기 쉽고 유지보수하기 쉽도록 만들어주는 다양한 메서드와 기능을 제공한다.
build.gradle
//test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
- 여기에 AssertJ도 포함되어 있다.
- AssertJ에서 제공하는 assertion이 가독성이 더 높은것을 볼 수 있다.
@Test
void getName(){
Americano americano = new Americano();
assertEquals(americano.getName(), "아메리카노");//Junit API
assertThat(americano.getName()).isEqualTo("아메리카노");//AssertJ
}
테스트 코드 작성 시작
4. 수동 테스트 VS 자동화된 테스트
수동테스트
@Test
void add_manual_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. 다른 사람이 테스트 코드를 봤을때 어떤것을 검증해야 하는지 뭐가 맞는 상황인지 틀린 상황인지 알 수 없다.
- 3. 무조건 성공하는 테스트 코드이다.
- 문제점
자동화된 테스트
@Test
void add(){
CafeKiosk cafeKiosk = new CafeKiosk();
cafeKiosk.add(new Americano());
assertThat(cafeKiosk.getBeverages().size()).isEqualTo(1);
assertThat(cafeKiosk.getBeverages().get(0).getName()).isEqualTo("아메리카노");
}
- 자동화된 테스트는 사람이 확인하지 않아도 된다.
5. 테스트 케이스 세분화하기
- 해피 케이스
- 예외 케이스
+ 경계값 테스트: 범위(이상, 이하, 초과, 미만), 구간 날짜 등
✏ 요구사항
가게 운영 시간(10:00 ~ 22:00) 외에는 주문을 생성할 수 없다.
위의 요구사항에서
해피케이스: 10:00 ~ 22:00 사이의 시간에 주문을 생성하는 경우
- 해피케이스의 경우 경계값이 존재한다면 경계값을 테스트 하는것이 좋은 방법이다.
- 경계값 테스트: 10:00 or 22:00에 대한 테스트
예외 케이스: 운영시간 외의 시간에 주문을 생성하는 경우
주문생성 method
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(LocalDateTime.now(), beverages);
}
- createOrder 실행시 현재 시간을 받아와서 현재 시간이 가게 운영시간안에 있는 확인하고 주문을 생성하는 메서드
해피케이스
@Test
void createOrder(){
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
Order order = cafeKiosk.createOrder();
assertThat(order.getBeverages()).hasSize(1);
assertThat(order.getBeverages().get(0).getName()).isEqualTo("아메리카노");
}
!이 경우의 문제점!
- createOrder 메소드가 현재 시간을 가지고 주문시간을 check해서 예외를 던지도록 구현되어 있기 때문에 가게 운영시간 안에서 수행할때만 성공하는 테스트가 되어버린다.
해결방법은?
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(LocalDateTime.now(), beverages);
}
- LocalDateTime은 실행할때마다 변경되는 값으로 이것이 문제가 되기 때문에 함수 외부에서 입력받도록 수정
수정한 method로 테스트 수행
경계값 테스트
@Test
void createOrderWithCurrentTime(){
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
Order order = cafeKiosk.createOrder(LocalDateTime.of(2023, 8, 4, 10, 0));//경계값 테스트
assertThat(order.getBeverages()).hasSize(1);
assertThat(order.getBeverages().get(0).getName()).isEqualTo("아메리카노");
}
예외 테스트
@Test
void createOrderOutsideOpenTime(){//예외 테스트
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
assertThatThrownBy(()-> cafeKiosk.createOrder(LocalDateTime.of(2023, 8, 4, 9, 59)))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
실제로 사용할때는 현재시간을 넣어주는 방식으로 사용
테스트 할때는 원하는 시간을 넣어서 테스트 하는 방식으로 해결할 수 있다.
Order order = cafeKiosk.createOrder(LocalDateTime.now());