오늘은 단위 테스트에 대해 알아보겠습니다.
단위 테스트란?
단위 테스트는 영어로 Unit Test입니다.
단위 테스트는 모듈 혹은 애플리케이션 안에 있는 개별적인 코드 단위가 의도한 대로 작동하는지 확인하는 행위입니다.
"뭔 말인가요? 더 쉽게 설명해 주세요!"
==> 하나의 애플리케이션에는 한 개 이상의 작동하는 기능들로 구성되어 있습니다.
단위 테스트란, 이런 기능들을 '개별적으로' 테스트함을 의미합니다.
"아~ 그러면 단위 테스트는, 기능들이 개별적으로 작동하는지 테스트하는 행위이군요."
맞습니다! 이렇게 생각하셨다면 단위 테스트의 뜻을 파악하신 것입니다👍
그렇다면 단위 테스트는 왜 사용할까요? 다음 예시를 통해 알아보겠습니다.
애니메이션을 바탕으로 프로그램 구현을 하는 '홍보와 졸부' 회사에 신입 개발자 제이가 들어왔습니다.
회사에 들어왔는데, 이오가 제이에게 새로운 프로그램 제작을 맡겼습니다.
이오 : [토끼와 거북이 경주 게임]을 다음과 같은 흐름으로 만들어주세요!
1. 토끼와 거북이의 이름은 각각 'R', 'T'로 시작됩니다.
2. 이동 시도할 회수를 받아주세요.
==> n 번의 라운드 동안 경주를 하게 됩니다.
==> "거리 설정은, 토끼가 빠르니깐.. 이동할 때마다 3씩 증가시켜주고, 거북이는 느리니깐 1씩 증가시켜주세요!"
3. 매 라운드마다 현재 상태를 출력해 주세요.
4. 우승자를 출력해 주세요.
이오가 위와 같은 프로그램을 만들어 달라고 요청을 했습니다!
이오의 요구사항에 맞게 프로그램을 만들었는데 잘 작동이 되는지 확인을 해볼까요?
(예상 결과 : Turtle 2, Rabbit 6 / Rabbit win)
실행을 시켜서 결과를 확인해 보면 위에 사진과 같이 프로그램이 잘 작동되는 것을 확인할 수 있습니다.
이오의 요구사항처럼 토끼는 이동 시마다 +3씩 증가하고, 거북이는 +1씩 증가했습니다.
프로그램을 다 만들었는데, 갑자기 우르가 새로운 요구사항을 들고 왔습니다.
우르 : 제이, 거북이가 너무 불쌍해요!
현재 거북이는 이동 시마다 +1씩 증가하지만, +2씩 증가하게 바꿔주세요.
우르의 요구사항에 맞게 다시 리팩토링을 진행했으니, 작동이 잘 되는지 확인해 봐야겠죠?
다시 한번 프로그램을 작동 시켜보겠습니다.
이런! 오도가 신입 개발자 제이를 방해하려고 결과를 보려면 1~100까지 입력하게 바꿔놨습니다!
마지막으로 코코닥이 새로운 요구사항을 들고 왔습니다.
코코닥 : 제이, 나는 평등한 게 좋으니 거북이와 토끼가 모두 움직일 때마다 +3씩 움직이게 만들어주세요!
마지막으로 코코닥의 요구 사항대로 리팩토링을 진행하고, 작동을 확인해 봤습니다.
제이는 눈물을 머금고 1~100까지의 숫자를 입력하고 리팩토링 한 결과를 확인했습니다.
신입 개발자 제이는 또 다른 요구사항에 대비를 해야 했지만, 오도의 방해로 걱정을 했습니다.
이를 타파하기 위해서 결국 밤 잠을 설치면서 열심히 구글링을 하며 '단위 테스트'를 공부하게 됩니다!
제이는 공부를 마치고, 코코닥의 요구사항에 맞게 한 번 이동 시마다 토끼와 거북이의 거리는 +3씩 증가하는 기능을 테스트하려고 합니다.
public class PlayersTest {
@Test
@DisplayName("코코닥의 요구사항에 맞게, 토끼와 거북이는 한 번 이동시마다 +3씩 거리가 증가한다!")
void test_moveAll() {
// given
int defaultDistance = 0;
int expectedDistance = 3;
Player rabbit = new Player("Rabbit", defaultDistance);
Player turtle = new Player("Turtle", defaultDistance);
List<Player> participants = List.of(rabbit, turtle);
Players racingPlayers = new Players(participants);
// when
racingPlayers.moveAll();
// then
assertThat(rabbit.getDistance()).isEqualTo(expectedDistance);
assertThat(turtle.getDistance()).isEqualTo(expectedDistance);
}
}
위와 같은 단위 테스트 작성을 통해서, 제이는 오도의 방해를 피해 리팩토링 한 코드의 결과를 확인할 수 있었습니다.
(단위 테스트 작성 방법은 아래에 기술하겠습니다.)
여기서 단위 테스트를 사용하면서 '신입 개발자 제이'는 어떤 결과를 얻었을까요?
- 1 ~ 100까지의 숫자를 입력하지 않아도 결과를 확인할 수 있었다.
- 추후에 새로운 요구사항으로 인해 리팩토링을 진행해도 결과를 바로 확인할 수 있다.
이렇게 두 가지 결과를 얻었습니다.
다시 이 내용을 조금 더 보편적인 말로 정리하자면 다음과 같습니다.
- 1 ~ 100까지의 숫자를 입력하지 않아도 결과를 확인할 수 있었다.
- ==> 기능의 일부를 빠르게 검증할 수 있다.
- 추후에 새로운 요구사항으로 인해 리팩토링을 진행해도 결과를 바로 확인할 수 있다.
- ==> 서비스 작동에 오류가 있을 시 코드의 문제점을 빠르게 확인할 수 있다.
- ==> 단위 테스트를 통해 리팩토링 및 기능 검증을 빠르게 진행할 수 있다.
자신감이 찬 개발자 제이는, 훗날 들어올 후임 개발자들을 위해 몇 가지 단위 테스트 꿀팁을 남겼습니다.
- 테스트 코드를 작성하기 위해서 [Given - When - Then] 패턴을 사용하면 편리하다!
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class PlayersTest {
@Test
@DisplayName("코코닥의 요구사항에 맞게, 토끼와 거북이는 한 번 이동시마다 +3씩 거리가 증가한다!")
void test_moveAll() {
// given (테스트를 위해 준비된 데이터 및 환경) : 준비
int defaultDistance = 0;
int expectedDistance = 3; // 기능 작동 후 예상하는 이동 거리
Player rabbit = new Player("Rabbit", defaultDistance);
Player turtle = new Player("Turtle", defaultDistance);
List<Player> participants = List.of(rabbit, turtle);
Players racingPlayers = new Players(participants);
// when (테스트 대상에게 메서드를 실행한다. == 기능을 동작한다.) : 실행
racingPlayers.moveAll();
// then (기능 동작 후, 의도한 결과가 나왔는지 검증한다.) : 검증
// 검증 1. 토끼의 이동 후 거리는 expectedDistance 변수의 값과 동일하다.
assertThat(rabbit.getDistance()).isEqualTo(expectedDistance);
// 검증 1. 거북이의 이동 후 거리는 expectedDistance 변수의 값과 동일하다.
assertThat(turtle.getDistance()).isEqualTo(expectedDistance);
}
}
예외를 찾는 경우에 given에 주어지는 값에 따라 테스트의 결과가 달라질 수 있습니다.
이런 경우, given에 따라서 테스트를 n 개를 만드는 것보다는, @ParameterizedTest 어노테이션을 이용해서 다양한 값을 넣어서 여러 예외를 잡을 수 있습니다.
3. 좋은 테스트를 위해선 FIRST 규칙을 따르자!
- Fast
- 좋은 단위 테스트는 내부 코드만 테스트를 진행하여 빠른 동작을 해야 한다.
- Independent
- 좋은 단위 테스트는 테스트하고자 하는 '단위 기능에 집중'해야 한다.
- Repeatable
- 좋은 단위 테스트는 반복 수행을 하더라도 같은 결과가 나와야 한다.
- Self-Validating
- 좋은 단위 테스트는 실패 혹은 성공을 해야 한다.
- 또한 사람이 콘솔 출력같이 수동적으로 검증하는 것이 아닌 테스트 자체적으로 결과가 나와야 한다.
- Timely
- 좋은 단위 테스트는 제때 미루지 않고 작성해야 한다.
'Develop > Java' 카테고리의 다른 글
[Java] 이름은 어렵지만 속은 쉬운 '제네릭' 알아보자! (0) | 2023.02.27 |
---|---|
[Java] 원시값 포장에 대해 알아보자 (2) | 2023.02.23 |
[Java] 만취한 사람도 쉽게 이해할 수 있는 '일급 컬렉션'에 대해 알아보자! (0) | 2023.02.13 |
[Java] final 키워드에 대해 알아보자 (0) | 2023.02.13 |
[Java] AssertJ 문법과 간단한 예시 (예외처리 검증 추가) (0) | 2023.02.13 |