Practical Testing: 실용적인 테스트 가이드 강의 - 대시보드 | 인프런 (inflearn.com)
Practical Testing: 실용적인 테스트 가이드 | 박우빈 - 인프런
박우빈 | 이 강의를 통해 실무에서 개발하는 방식 그대로, 깔끔하고 명료한 테스트 코드를 작성할 수 있게 됩니다. 테스트 코드가 왜 필요한지, 좋은 테스트 코드란 무엇인지 궁금하신 모든 분을
www.inflearn.com
본 내용은 'Practical Testing: 실용적인 테스트 가이드' 라는 인프런 강의를 들으며 내 생각을 정리한 내용이다.
테스트 코드에 대해서 들어도 보았고, 중요하다는것은 알았지만,
어떤 형식으로 작성해야 할 지, 내가 제대로 이해하고 있는것은 맞는지 배우고자 수강했다.
나에게 테스트 코드란
필자는 테스트 코드라는 단어를 들어보기만 했지, 내 구현 코드를 위한 테스트코드를 실질적으로 작성해본적은 없었다.
그러다 작년 겨울 우아한테크코스 코딩 테스트에 참여하여 테스트 코드에 대해서 고민해보게 되면서 그 중요성을 조금씩 알아가게 되었다.
예전에는 나에게 테스트 코드란 '구현하기도 벅찬 나같은 사람이 아닌, 능력자들의 마지막 1% 같은 것'으로 보였지만,
지금은 '당장은 느려보일 수 있으나, 내 코드에 신뢰를 가질 수 있고, 내가 놓친 것들을 한번 더 생각하게 만드는 수단' 이라고 생각한다.
테스트 코드는 왜 필요할까?
기능이 작을 때에는 수동으로 프로그램을 테스트를 어렵지 않게 수행 할 수 있다. 하지만, 기능이 점점 거치면서 수동으로 시행하기에는 너무 많은 테스트 케이스들이 생겨나게 된다. 이것들을 사람들이 일일이 테스트하기에는 시간과 비용이 들 뿐만 아니라, 실수도 필연적으로 생길 수 있다.
그렇기에 테스트 코드를 작성하여 사람이 수동으로 체크해야 할 사항들을 컴퓨터가 체크하게 함으로써 내 코드에 대한 신속한 피드백을 통해 코드 품질을 향상시킬 수 있다.
하지만 내가 무작정 테스트 코드를 작성한다고 해서, 수동 테스트의 단점이 전부 해결되는것은 아니다. 테스트 코드를 '잘' 작성해야지 수동 테스트가 가지는 어려움들을 해소할 수 있다.
단위 테스트
단위 테스트(Unit test)
작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트를 의미한다.
또한, 테스트 방식 중 규모가 가장 작은 테스트 방식이다.
수동 테스트 ,자동화 테스트
수동 테스트는 사람이 직접 수동으로 프로그램을 실행하면서 검증하는 것 이라면,
자동 테스트는 Junit5와 같은 테스트 프레임워크를 통하여 개발자가 작성한 케이스에 한하여 기계가 검증을 하는것을 의미한다.
또한, 모든 상황에서 수동 테스트 보다 자동화 테스트가 좋은 것은 아니기에
테스트 코드가 필요한 상황과 그렇지 않은 상황을 구분할 줄 아는 능력이 필요하다.
해피 케이스, 예외 케이스, 경계값 테스트
해피 케이스는 테스트 코드가 통과하는 케이스를 의미한다.
우리가 아메리카노를 주문하는 코드를 작성한다고 할 때, 다음과 같은 해피 케이스 테스트 코드를 작성할 수 있다.
@Test
void addSeveralBeverages() {
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano, 2);
assertThat(cafeKiosk.getBeverages()).size().isEqualTo(2);
assertThat(cafeKiosk.getBeverages().get(0)).isEqualTo(americano);
assertThat(cafeKiosk.getBeverages().get(1)).isEqualTo(americano);
}
예외 케이스는 코드에 예상치 못한 값을 넣었을 때, 그 값을 올바르게 처리할 수 있는지 테스트하는 케이스를 의미한다.
아래는 0잔의 아메리카노를 주문했을 때, 적절한 메시지가 출력되는지 확인하는 테스트 코드이다.
@Test
void addZeroBeverages() {
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
assertThatThrownBy(() -> cafeKiosk.add(americano, 0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("음료는 1잔 이상 주문하실수 있습니다.");
}
그리고 이런 케이스들을 테스트 할 때는 경계값을 테스트하는 것이 가장 중요하다.
예를들어서, 어떤 값이 3 이상 일 때, A라는 조건을 만족해야 한다면,
우리는 이 코드의 해피 케이스를 작성할 때, 4나 5 같은 수 보다는 경계값인 3으로 테스트 케이스를 작성함으로써,
지정한 범위 내에 코드가 정확히 동작하는지 파악 할 수 있다.
마찬가지로 예외 케이스를 작성 할 때, -1 이나 1을 이용하여 테스트 하기 보다는 경계값인 2를 테스트 하는것이 좋다.
따라서 경계값이 존재하는 테스트 케이스는 경계값으로 테스트를 작성하는것이 중요하다.
테스트하기 어려운 부분을 분리하기
강의에서는 커피 가게의 오픈 시간을 예로 들어서 설명했다.
오전 10시 이전, 오후 10시 이후에는 "주문 시간이 아닙니다. 관리자에게 문의하세요." 라는 예외를 발생한다.
@Getter
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);
private final List<Beverage> beverages = new ArrayList<>();
...
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);
}
}
그리고 테스트하기 어려운 부분을 분리하지 않고 테스트 코드를 작성했다.
이 코드는 어쩔 때에는 통과하고, 어쩔 때에는 실패하게 된다. 이유가 뭘까?
CafeKiosk의 createOrder 내부 구현 때문이다.
createOrder는 현재 LocalDateTime.now()를 이용하여 주문을 생성하기 때문에, 우리가 테스트 하는 시간에 따라서 테스트가 통과할 수도, 실패할 수도 있게 된다.
@Test
void createOrder() {
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
assertThatThrownBy(() -> cafeKiosk.createOrder())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
이것을 해결하기 위해서는 테스트하기 어려운 영역(현재시간, LocalDateTime.now())를 기존 코드에서 제거하고,
메서드 외부에서 값을 주입받을 수 있도록 해야한다.
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, 3, 27, 9, 59)))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
TDD: Test Driven Development
구현할 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현을 주도하도록 하는 방법론을 의미한다.
쉽게 말하면 기능을 구현하고 테스트 코드를 작성하는 방식이 아닌,
테스트 코드를 먼저 작성하고, 테스트 결과를 피드백 받으면서 구현 코드를 완성해가는 방식이다.
레드-그린-리펙토링
TDD를 수행하기 위한 기법이다. 레드 -> 그린 -> 리펙토링 순서대로 진행한다.
RED : 실패하는 테스트를 작성한다
실패하는 테스트를 작성하는 말이 책으로만 보면 무슨말인지 잘 이해가 되지 않았었다. 본 강의에서는 다음과 같은 예를 제시했다.
우리가 calculateTotalPrice라는 기능을 구현하려 할 때, 먼저 테스트 코드를 작성하여 기능이 없는 빈 메서드를 생성한다.
이런 상태에서 코드를 실행하면 RED,
즉, 실패하는 테스트를 작성하는것을 완료하게 된다.
@Test
void calculateTotalPrice() {
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
Latte latte = new Latte();
cafeKiosk.add(americano);
cafeKiosk.add(latte);
int totalPrice = cafeKiosk.createTotalPrice();
assertThat(totalPrice).isEqualTo(8500);
}
public class CafeKiosk {
...
public int createTotalPrice() {
return 0;
}
}
GREEN : 테스트를 통과하는 최소한의 코드를 작성한다
강의에서는 다음과 같은 예를 제시했다.
극단적인 예이긴 하지만, 이것도 맞는 방법이라고 한다.
public class CafeKiosk {
...
public int createTotalPrice() {
return 8500;
}
}
REFACTORING : 구현 코드를 개선하여 테스트 통과를 유지한다.
이런식으로 코드를 리팩토링하여 여전히 테스트가 통과하도록 유지한다.
public int createTotalPrice() {
return beverages.stream().mapToInt(Beverage::getPrice)
.sum();
}
1일차를 마치며
이 강의에서 좋은점이 각 섹션 마지막에는 키워드를 정리해 주는데 이 방식이 좋은것 같다. 추가적으로 알면 좋을 키워드들도 던져주면서 지식의 범위를 넓혀 주는데 아무래도 연관된 키워드다 보니 동기 부여가 되어서 , 그냥 무작정 자료들을 검색하는것들 보다 더 몰입하면서 공부할 수 있었다.
참고 자료
Practical Testing: 실용적인 테스트 가이드 강의 - 대시보드 | 인프런 (inflearn.com)
Practical Testing: 실용적인 테스트 가이드 | 박우빈 - 인프런
박우빈 | 이 강의를 통해 실무에서 개발하는 방식 그대로, 깔끔하고 명료한 테스트 코드를 작성할 수 있게 됩니다. 테스트 코드가 왜 필요한지, 좋은 테스트 코드란 무엇인지 궁금하신 모든 분을
www.inflearn.com
https://testmanager.tistory.com/186
자동화 된 테스트 vs 수동 테스트 : 차이점
수동 테스트 란 무엇입니까?수동 테스트는 QA 분석가가 테스트를 수동으로 실행하는 소프트웨어 테스트입니다. 개발중인 소프트웨어에서 버그를 발견하기 위해 수행됩니다.수동 테스트에서 테
testmanager.tistory.com
'Java > 테스트 코드' 카테고리의 다른 글
Practical Testing: 실용적인 테스트 가이드 (2일차) (0) | 2024.04.04 |
---|