항상 나는 고민이 있었다.
랜덤한 값을 발생시키는 애플리케이션의 경우, 테스트를 어떻게 해야하는가?
오늘 Honux 의 테스트코드 수업을 듣고 큰 깨달음을 얻어 이렇게 정리한다.
import java.util.Random;
public class Dice {
private final Random random = new Random();
public int roll() {
return random.nextInt(7);
}
}
Dice
클래스
- 주사위를 던지는
roll()
은 1 ~ 6 범위의 랜덤 정수를 반환한다.
roll()
메서드에 대한 테스트는 다음과 같이 작성할 수 있을 것이다.class DiceTest {
@Test
@DisplayName("주사위를 굴리면 1 ~ 6 사이의 숫자가 나온다.")
void roll() {
Dice dice = new Dice();
assertThat(dice.roll()).isLessThanOrEqualTo(6);
assertThat(dice.roll()).isGreaterThanOrEqualTo(1);
}
}
숫자 범위가 1 ~ 6 사이인지 테스트할 수 있다.
Game
클래스를 만든다.public class Game {
private final Dice dice;
public Game(Dice dice) {
this.dice = dice;
}
public boolean isEven() {
return dice.roll() % 2 == 0;
}
}
Game
클래스
- 주사위값이 짝수인 경우,
true
를 반환하는isEven()
메서드 구현
isEven()
메서드는 dice.roll()
의 결과값에 의존한다.
이때 dice.roll()
은 결과값을 예측할 수 없는 랜덤한 요소이다.
테스트코드는 isEven()
의 실제 결과값과 내가 예측한 결과값을 비교하여야 하는데,
class GameTest {
@Test
@DisplayName("주사위 값이 짝수인 경우 true를 반환하는 지 확인하고 싶은데...")
void isEvenBadTest() {
Game game = new Game(new Dice());
for (int i = 0; i < 100; i++) {
System.out.println(game.isEven());
}
}
}
- 이렇게 눈으로 결과를 확인하는 코드는 테스트코드가 아니다.
- 심지어 주사위값이 1 ~ 6 중 무엇인지조차 알 수 없다.
- 그래서
isEven()
메서드가 잘 만들어졌는지 확인할 수 없다.
true
false
true
true
false
true
false
true
true
false
true
false
false
true
...
실행 결과
실제 객체로 테스트하기 어려운 경우가 있다.
이런 경우에 예측 가능한 가짜 객체를 만들어서 테스트를 수행할 수 있다.
class GameTest {
@Test
@DisplayName("주사위 값이 짝수인 경우 true를 반환한다.")
void isEven() {
Game game = new Game(new Dice() {
Random random = new Random();
@Override
public int roll() {
return (random.nextInt(3) + 1) * 2; // 2, 4, 6 만 반환하는 가짜 주사위 객체
}
});
assertThat(game.isEven()).isTrue();
}
@Test
@DisplayName("주사위 값이 홀수인 경우 false를 반환한다.")
void isOdd() {
Game game = new Game(new Dice() {
Random random = new Random();
@Override
public int roll() {
return (random.nextInt(3) + 1) * 2 - 1; // 1, 3, 5 만 반환하는 가짜 주사위 객체
}
});
assertThat(game.isEven()).isFalse();
}
}
정상적인 테스트코드
- 기존의
Dice
객체를 상속하는 가짜 주사위 객체를 만들어 테스트를 진행한다.
- 가짜 주사위 객체는 메서드가 정상적으로 동작하는지 확인하기 위해 내가 예측가능한 값을 반환해야 한다.
Game
클래스에서dice.roll()
을 호출하면, 실제 인스턴스인 가짜 주사위 객체의roll()
이 호출된다.
출처 : 코드스쿼드 마스터즈 코스 - 백엔드 마스터 Honux 수업