테스트하기 어려운 코드가 생기면 인터페이스 추출을 생각하자!
다음 Car 객체의 move()
메소드의 이동/정지를 테스트하고 싶은데 테스트하기 힘들다. move()
메소드 내에 random
값이 생성되고 있기 때문이다. move()
메소드를 테스트 하기 위해선 어떻게 해야할까? 단, random
값을 메소드의 인자로 전달해 해결하면 안 된다.
힌트: 인터페이스를 활용한다.
public class Car {
private static final int FORWARD_NUM = 4;
private static final int MAX_BOUND = 10;
private int position = 0;
public void move() {
if (getRandomNo() >= FORWARD_NUM)
this.position++;
}
private int getRandomNo() {
Random random = new Random();
return random.nextInt(MAX_BOUND);
}
}
MoveStrategy: Car에서 도출 —> 인터페이스
RandomMoveStrategy: 클라이언트 요구에 의한 도출 —> MoveStrategy를 구현한 클래스
public class Car {
private int position;
public Car() {
this.position = 0;
}
public void move(MoveStrategy moveStrategy) {
if (moveStrategy.isMovable()) {
this.position++;
}
}
public int getPosition() {
return position;
}
}
public interface MoveStrategy {
boolean isMovable()
}
import java.util.Random;
public class RandomMoveStrategy implements MoveStrategy {
private static final int FORWARD_NUM = 4;
private static final int MAX_BOUND = 10;
@Override
public boolean isMovable() {
return generateRandomNumber() >= FORWARD_NUM;
}
private int generateRandomNumber() {
Random random = new Random();
return random.nextInt(MAX_BOUND);
}
}
Car.move()
를 테스트 할 때는 테스트 내에서 특정 숫자를 반환하는 MoveStrategy
객체를 구현 한 후 Car.move()
의 인자로 주입해주면 된다.
class CarTest {
public static final int MOVE_FORWARD_NUM = 4;
public static final int STAY_NUM = 3;
private Car car;
@BeforeEach
void setUp() {
car = new Car();
}
@Test
@DisplayName("5이상이면 전진한다")
void move_forward() {
car.move(new TestMoveStrategy(MOVE_FORWARD_NUM));
assertThat(car.getPosition()).isEqualTo(1);
}
@Test
@DisplayName("5 미만이면 전진하지 않는다.")
void stay() {
car.move(new TestMoveStrategy(STAY_NUM));
assertThat(car.getPosition()).isEqualTo(0);
}
}
class TestMoveStrategy implements MoveStrategy {
private static final int FORWARD_NUM = 4;
private final int number;
public TestMoveStrategy(int number) {
this.number = number;
}
@Override
public boolean isMovable() {
return number >= FORWARD_NUM;
}
}
TestMoveStrategy
를 따로 만들지 않고 람다식을 이용해 테스트해도 된다.
class CarTest {
private Car car;
@BeforeEach
void setUp() {
car = new Car();
}
@Test
@DisplayName("전진한다")
void move_forward() {
car.move(() -> true);
assertThat(car.getPosition()).isEqualTo(1);
}
@Test
@DisplayName("전진하지 않는다.")
void stay() {
car.move(() -> false);
assertThat(car.getPosition()).isEqualTo(0);
}
}