본문 바로가기

테스트

[Junit] 랜덤값 테스트 방법

TDD를 공부하기 위해서 우테코 프리코스 문제를 가지고 연습하고 있었는데,

랜덤값을 사용해서 로직을 구현해야했다.

랜덤값을 사용해야한다면 테스트 코드를 작성하기 힘들기때문에 랜덤값을 외부에서 주입하도록 코드를 작성했음에도 불구하고, 로직에 랜덤값이 들어가야 하는 상황이 발생했다.

랜덤값을 사용해야하는 객체는 랜덤값을 생성하는 기능을 가진 함수형 인터페이스를 의존하고 DI로 구현체를 주입해서 해결했다.


 

NumberGenerator 인터페이스 생성

@FunctionalInterface
public interface NumberGenerator {
    int generate();
}

 

람다식을 이용하기 위해 @FunctionalInterface 어노테이션을 선언해 해당 인터페이스가 함수형 인터페이스라는것을 알려준다.

 


 

RandomNumberGenerator 구현체 생성

public class RandomNumberGenerator implements NumberGenerator{
    
    public static final int START_NUMBER = 0;
    public static final int END_NUMBER = 9;

    @Override
    public int generate() {
        return Randoms.pickNumberInRange(START_NUMBER, END_NUMBER);
    }
}

 

0에서 9사이의 랜덤숫자를 리턴하는 메소드를 구현했다.


랜덤값을 사용하는 객체는 NumberGenerator 인터페이스를 의존

public class Cars {

    public static final int MIN_SIZE = 2;
    private final List<Car> cars;
    private final MoveCondition moveCondition;
    private final NumberGenerator numberGenerator;

    private Cars(final List<Car> userCars, final MoveCondition moveCondition, final NumberGenerator numberGenerator) {
        this.cars = userCars;
        this.moveCondition = moveCondition;
        this.numberGenerator = numberGenerator;
    }

    public static Cars of(final List<Car> userCars, final MoveCondition moveCondition, final NumberGenerator numberGenerator){
        validateSize(userCars);
        return new Cars(userCars, moveCondition, numberGenerator);
    }
    
}

 


인터페이스 메소드를 이용해서 로직구현

    public RoundResult play() {
        if (moveCondition.isValid(numberGenerator.generate())) {
            for (Car car : cars) {
                car.move();
            }
        }
        return new RoundResult(parseToResult());
    }

 


테스트코드

public class CarsTest {

	@Test
    void 숫자가_4미만일경우에_전진하지_않는다() {
        //given
        Cars cars = Cars.of(
                List.of(new Car("pobi"), new Car("cobi")),
                new MoveCondition(),
                () -> 3); // 함수형 인터페이스는 람다식을 이용해서 구현할 수 있다.
        //when
        RoundResult roundResult = cars.play();
        //then
        Map<String, Integer> result = roundResult.getResult();
        Assertions.assertThat(result.get("pobi")).isEqualTo(0);
        Assertions.assertThat(result.get("cobi")).isEqualTo(0);
    }

    @Test
    void 숫자가_4이상일경우에_전진한다() {
        //given
        Cars cars = Cars.of(
        		List.of(new Car("pobi"), new Car("cobi")),
        		new MoveCondition(),
                () -> 4); // 함수형 인터페이스는 람다식을 이용해서 구현할 수 있다.
        //when
        RoundResult roundResult = cars.play();
        //then
        Map<String, Integer> result = roundResult.getResult();
        Assertions.assertThat(result.get("pobi")).isEqualTo(1);
        Assertions.assertThat(result.get("cobi")).isEqualTo(1);
    }


}

 

함수형 인터페이스는 람다식을 이용해서 구현할 수 있기때문에,

'숫자가 4미만일경우에 전진하지 않는다' 라는 테스트 코드에는 () -> 3 이라고 표기해 항상 랜덤값이 3으로 유지되게 했다.

'숫자가 4이상일경우에 전진한다' 라는 테스트 코드에는 () -> 4 이라고 표기해 항상 랜덤값이 4으로 유지되게 했다.