들어가며
이전에 'Parameterized Test - 테스트를 효율적으로'라는 글을 올렸었는데 전부 학습한 내용이 아니라서 이번에 사용한 기능을 추가로 포스팅하려고 합니다.
이전에 다뤘던 테스트는 클래스, 문자열, 기본 타입, Enum, Csv를 사용한 테스트였습니다. 하지만 좀 더 복잡한 객체들을 파라미터로 테스트할 수 있는 방법이 있습니다. 테스트 코드를 예시로 보면서 설명하겠습니다. 로또 번호를 맞추는 테스트이고 맞힌 숫자에 따라 등수를 반환합니다. (실제 로또와 같은 원리로 동작하는 코드입니다.)
private static final Number BONUS = new Number(7);
private static final Lotto LOTTO = new Lotto(givenNumbers(1, 2, 3, 4, 5, 6));
private static final WinnerLotto WINNER_LOTTO = new WinnerLotto(LOTTO, BONUS);
@Test
@DisplayName("숫자가 전부 일치하면 1등을 반환한다.")
void countEqualsLottoNumbers() {
assertThat(WINNER_LOTTO.findRank(LOTTO)).isEqualTo(Rank.FIRST);
}
@Test
@DisplayName("숫자가 5개 일치하고 보너스 숫자를 포함하면 2등을 반환한다.")
void containsNumbersAndBonusNumber() {
Lotto lotto = new Lotto(givenNumbers(1, 2, 3, 4, 5, 7));
assertThat(WINNER_LOTTO.findRank(lotto)).isEqualTo(Rank.SECOND);
}
@Test
@DisplayName("숫자가 5개 일치하면 3등을 반환한다.")
void containsNumbersFiveReturnThirdNotContainsBonus() {
Lotto lotto = new Lotto(givenNumbers(1, 2, 3, 4, 5, 9));
assertThat(WINNER_LOTTO.findRank(lotto)).isEqualTo(Rank.THIRD);
}
@Test
@DisplayName("숫자가 4개 일치하면 4등을 반환한다.")
void containsNumbersFourReturnFourth() {
Lotto lotto = new Lotto(givenNumbers(1, 2, 3, 4, 9, 10));
assertThat(WINNER_LOTTO.findRank(lotto)).isEqualTo(Rank.FOURTH);
}
@Test
@DisplayName("숫자가 3개 일치하면 5등을 반환한다.")
void containsNumbersThreeReturnFifthNotContainsBonus() {
Lotto lotto = new Lotto(givenNumbers(1, 2, 3, 8, 9, 10));
assertThat(WINNER_LOTTO.findRank(lotto)).isEqualTo(Rank.FIFTH);
}
@Test
@DisplayName("숫자가 2개 이하로 일치하면 None을 반환한다.")
void containsNumbersSecondReturnNone() {
Lotto lotto = new Lotto(givenNumbers(1, 2, 8, 9, 10, 11));
assertThat(WINNER_LOTTO.findRank(lotto)).isEqualTo(Rank.NONE);
}
private static List<Number> givenNumbers(int... numbers) {
return Arrays.stream(numbers)
.mapToObj(Number::new)
.collect(Collectors.toList());
}
new Lotto()로 생성되는 코드와 순위 빼고는 모두 중복이 되는 코드입니다. 하지만 Lotto 객체를 파라미터로 받아야 하고 그에 맞게 Enum타입의 Rank를 받아야 해서 기존에 알고 있는 @ParameterizedTest의 지식으로는 해결이 불가능한 줄 알았습니다.
@MethodSource
@MothodSouce를 사용해 복잡한 인수들을 파라미터로 넘길 수 있습니다. Stream 를 반환하는 static 메서드를 작성해주면 됩니다.
of 메서드나 arguments 메서드를 보면 Object를 가변 인자로 받기 때문에 다양한 타입을 사용하여 Arguments를 생성할 수 있습니다.
static Stream<Arguments> lottoNumbersAndRank() {
return Stream.of(
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 6)), Rank.FIRST),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 7)), Rank.SECOND),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 9)), Rank.THIRD),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 9, 10)), Rank.FOURTH),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 8, 9, 10)), Rank.FIFTH),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 8, 9, 10, 11)), Rank.NONE)
);
}
@ParameterizedTest(name = "로또번호 : {0}, 결과 : {1}")
@MethodSource("lottoNumbersAndRank")
@DisplayName("맞춘 번호에 따라 등수를 반환한다.")
void findRank(Lotto lotto, Rank rank) {
assertThat(WINNER_LOTTO.findRank(lotto)).isEqualTo(rank);
}
private static List<Number> givenNumbers(int... numbers) {
return Arrays.stream(numbers)
.mapToObj(Number::new)
.collect(Collectors.toList());
}
static Stream<Arguments> lottoNumbersAndRank() {
return Stream.of(
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 6)), Rank.FIRST),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 7)), Rank.SECOND),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 9)), Rank.THIRD),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 9, 10)), Rank.FOURTH),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 8, 9, 10)), Rank.FIFTH),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 8, 9, 10, 11)), Rank.NONE)
);
}
@MethodSouce안에 static으로 선언한 메서드명을 작성합니다. 메서드명을 올바르게 작성하고, 인텔리제이 Ultimate 버전이라면 단축키를 통해 메서드가 선언된 곳으로 이동이 가능합니다. (커뮤니티 버전은 되는지 모르겠습니다.) 잘 못 선언하면 인텔리제이가 경고를 해주니 타입 세이프하다고 해야 할지 잘 모르겠네요. 이제 테스트를 해보면 Stream.of로 선언한 Arguments의 순서대로 테스트 코드의 파리터로 값이 전달됩니다.
기존 반복되는 코드들을 줄이게 되었습니다.
@ArgumentsSource
메서드로 선언하지 않고 클래스로 선언하는 방법도 있습니다. ArgumentsProvider 인터페이스를 구현한 클래스를 @ArgumentsSource 어노테이션에 선언해주면 됩니다.
public class LottoNumberArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 6)), Rank.FIRST),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 7)), Rank.SECOND),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 5, 9)), Rank.THIRD),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 4, 9, 10)), Rank.FOURTH),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 3, 8, 9, 10)), Rank.FIFTH),
Arguments.arguments(new Lotto(givenNumbers(1, 2, 8, 9, 10, 11)), Rank.NONE)
);
}
private static List<Number> givenNumbers(int... numbers) {
return Arrays.stream(numbers)
.mapToObj(Number::new)
.collect(Collectors.toList());
}
}
@ParameterizedTest(name = "로또번호 : {0}, 결과 : {1}")
@ArgumentsSource(LottoNumberArgumentsProvider.class)
@DisplayName("맞춘 번호에 따라 등수를 반환한다.")
void findRank(Lotto lotto, Rank rank) {
assertThat(WINNER_LOTTO.findRank(lotto)).isEqualTo(rank);
}
'개발 > Java' 카테고리의 다른 글
[Java] 데이터 타입, 변수 (0) | 2022.04.16 |
---|---|
[Java] 제네릭 (2) | 2022.03.02 |
[Java] package, import (2) | 2022.02.15 |
[JUnit] Parameterized Test - 테스트를 효율적으로 (1) | 2022.02.11 |
[Java] 리플렉션 (reflection) 개념 이해하기 (0) | 2022.01.29 |