자동차의 움직임에 대한 메서드를 테스트하고 싶은데.. 자동차는 움직일 때 Random 값을 받아서 움직이고 있다. 이때 자동차의 움직임을 테스트하기 위해서는 Random값을 조절해야하는가? 어떻게 진행 해야 할까?
실행 중에 알고리즘을 선택할 수 있게 하는 행위 소프트웨어 디자인 패턴이다. 즉, 컴파일 타임에서는 인터페이스를 매개변수로 받기 때문에 해당 구현체를 자세하게 모르지만 런타임에서 해당 매개변수에 들어가는 인터페이스의 구현체를 통해서 실제 작동할 알고리즘이 선택되는 패턴이다.
public boolean move() {
if (isMoveForward(generateRandomNumber())) {
position.increase();
return true;
}
return false;
}
}
여기서 generateRandomNumber()은 항상 Random 값을 가지고 오기 때문에 Test할 때 값을 예측할 수 없어서 Test가 불가능하다. 이에 전략패턴을 활용해서 인터페이스로 분리해 보았다.
public interface NumberGenerator {
int generate();
}
public class MovableNumberGenerator implements NumberGenerator {
private static final int EXCLUSIVE_BOUND_CORRECTION_VALUE = 1;
private static final int MINIMUM = 4;
private static final int MAXIMUM = 9;
private static final Random RANDOM = new Random();
@Override
public int generate() {
int randomNumber =
RANDOM.nextInt(MAXIMUM - MINIMUM + EXCLUSIVE_BOUND_CORRECTION_VALUE) + MINIMUM;
validateRange(randomNumber);
return randomNumber;
}
private static void validateRange(int randomNumber) {
if (randomNumber > MAXIMUM || randomNumber < MINIMUM) {
throw new IllegalArgumentException(ErrorMessage.RANGE_OVER.toString());
}
}
}
public class UnMovableNumberGenerator implements NumberGenerator {
private static final int EXCLUSIVE_BOUND_CORRECTION_VALUE = 1;
private static final int MINIMUM = 0;
private static final int MAXIMUM = 3;
private static final Random RANDOM = new Random();
@Override
public int generate() {
int randomNumber =
RANDOM.nextInt(MAXIMUM - MINIMUM + EXCLUSIVE_BOUND_CORRECTION_VALUE) + MINIMUM;
validateRange(randomNumber);
return randomNumber;
}
private static void validateRange(int randomNumber) {
if (randomNumber > MAXIMUM || randomNumber < MINIMUM) {
throw new IllegalArgumentException(ErrorMessage.RANGE_OVER.toString());
}
}
}
이렇게 인터페이스를 매개변수로 받는 방식으로 코드를 수정한다면 실제 구현되는 프로덕션 코드에서는
랜덤 숫자를 제공하는 인터페이스 구현체인 RandomNumberGenerator
객체로 생성된 숫자 넘겨주면 평소와 같이 그대로
랜덤 값을 통해서 자동차가 움직이게 된다.
public boolean move(int number) {
if (isMoveForward(number) {
position.increase();
return true;
}
return false;
}
}
하지만 Test 코드를 작성할 때는 MovableNumberGenerator
, UnMovableNumberGenerator
로 생성된 숫자를
매개변수 값에 넣어주게 되면 항상 움직이는 결과와, 항상 움직이지 않는 결과를 예측할 수 있게 된다.
이를 이용해서 자동차의 움직임을 테스트 할 수 있게 된다.
@DisplayName("move() 움직임 테스트")
@Test
public void move_with_movableNumber_test() throws Exception {
String name = "name1";
CarName carName = new CarName(name);
Position zeroPosition = new Position(0);
Car car = new Car(carName, zeroPosition);
NumberGenerator numberGenerator = new MovableNumberGenerator();
Car movedCar = car.move(numberGenerator.generate());
assertThat(movedCar.compareTo(car)).isGreaterThan(0);
}
@DisplayName("move() 정지 테스트")
@Test
public void move_with_unMovableNumber_test() throws Exception {
String name = "name1";
CarName carName = new CarName(name);
Position zeroPosition = new Position(0);
Car car = new Car(carName, zeroPosition);
NumberGenerator numberGenerator = new UnMovableNumberGenerator();
Car movedCar = car.move(numberGenerator.generate());
assertThat(movedCar.compareTo(car)).isEqualTo(0);
}