Junit으로 parameterized test

요약

@ParameterizedTest 어노테이션과 함께 @MethodSorce나 @ValueSource를 통해 한 테스트 메서드에 여러번 파라미터를 넘길 수 있다.

@MethodSource

도입 배경

자동차 이름의 글자 갯수가 5개 이하여야 하고, 자동차 이름이 빈 문자열이면 안되고 등등 다양한 제약 사항을 테스트해보고 싶다고 하자.
근데 테스트 코드를 작성하다보니 다음과 같이 메서드가 비슷비슷하게 나올 때 이를 리팩토링해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@SuppressWarnings("NonAsciiCharacters")
public class CarNameTest {

@Test
public void 자동차_이름_글자수_테스트() {
assertThatThrownBy(() -> new CarName("다섯글자이상"))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("자동차 이름은 5자 이하여야 합니다.");
}

@Test
public void 자동차_이름이_존재하지_않는_경우_테스트() {
assertThatThrownBy(() -> new CarName(""))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("모든 자동차 이름은 반드시 존재해야 합니다.");
}

// 등등...
}

해결하기

주목할 점은 한 테스트 메서드에 여러개의 인자를 여러 케이스로 보낼 수 있다는 점이다!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CarNameTest {

private static Stream<Arguments> provideCarNameAndExceptionMessage() {
return Stream.of(
Arguments.of(null, "자동차 이름을 찾을 수 없습니다!"),
Arguments.of("다섯글자이상", "자동차 이름은 5자 이하여야 합니다."),
Arguments.of("", "모든 자동차 이름은 반드시 존재해야 합니다."),
Arguments.of(" ", "자동차 이름은 공백으로 설정할 수 없습니다."),
Arguments.of(" ", "자동차 이름은 공백으로 설정할 수 없습니다.")
);
}

@ParameterizedTest
@MethodSource(value = "provideCarNameAndExceptionMessage")
public void 자동차_이름_예외_테스트(String carName, String message) {
assertThatThrownBy(() -> new CarName(carName))
.isInstanceOf(CarNameException.class)
.hasMessageContaining(message);
}
}

다음같이 @ParameterizedTest해서 @MethodSource로 해결할 수 있다!

@ValueSource

도입 배경

한가지 메서드에 여러가지 경우의 수를 넣어 실험하고 싶은데 어떻게 할까?
예를 들어 자동차 이름에 여러가지 이름을 넣어보고 실제로 들어갔는지 테스트해보고 싶으면 어떻게 할지 고민해보자.

1
2
3
4
5
6
7
8
9
10
11
@Test
public void 자동차_이름_테스트() {
Car klayCar = new Car("klay", 0);
assertThat(klayCar.getName()).isEqualTo("klay");

Car eveCar = new Car("eve", 0);
assertThat(eveCar.getName()).isEqualTo("eve");

Car pobiCar = new Car("pobi", 0);
assertThat(pobiCar.getName()).isEqualTo("pobi");
}

이렇게 무식하게 하면 당연히 뚜들겨 맞는다.

해결하기

주목할점은 전달될 인자의 자료형에 따라 strings, ints 등 필드값을 바꿔주면 된다.

1
2
3
4
5
6
@ParameterizedTest
@ValueSource(strings = {"클레이", "이브", "포비"})
public void 자동차_이름_테스트(String carName) {
Car car = new Car(carName, 0);
assertThat(car.getName()).isEqualTo(carName);
}

@EnumSource

열거형도 파라미터로 전달해줄 수 있다.

1
2
3
4
5
6
@ParametherizedTest
@EnumSource(value = CardFace.class)
void testAllSuit(CardFace face) {
int cardScore = face.getScore();
assertThat(cardScore > 0 && cardScore < 11).isTrue();
}

names 필드를 통해 열거형에서 일부만 골라서 넘겨줄 수 있다.

1
2
3
4
5
6
@ParametherizedTest
@EnumSource(value = CardFace.class, names = {"TEN", "KING", "QUEEN", "JACK"})
void testAllSuit(CardFace face) {
int cardScore = face.getScore();
assertThat(cardScore == 10).isTrue();
}

mode 필드를 사용하면 일부를 제외하고 넘겨줄 수 있다.

1
2
3
4
5
6
7
8
9
@ParametherizedTest
@EnumSource(
value = CardFace.class,
names = {"TEN", "KING", "QUEEN", "JACK"},
mode = EnumSource.Mode.EXCLUDE)
void testAllSuit(CardFace face) {
int cardScore = face.getScore();
assertThat(cardScore != 10).isTrue();
}

@CsvSource

CsvSource는 여러 값을 문자열로 작성해주면 알아서 맞는 매개변수로 바꿔서 가져온다.

1
2
3
4
5
6
7
8
9
10
11
12
@ParameterizedTest
@CsvSource(value = {"ACE,FIVE,true", "ACE,SIX,false"})
void canReceiveCard(CardFace face1, CardFace face2, boolean expected) {
final Participator dealer = new Dealer();
final Card firstCard = new Card(SPADE, face1);
final Card secondCard = new Card(SPADE, face2);

dealer.receiveCard(firstCard);
dealer.receiveCard(secondCard);

assertThat(dealer.canReceiveCard()).isEqualTo(expected);
}

파라미터 테스트 이름 설정하기

@ParameterizedTest의 name 필드에 {매개변수인덱스}를 넘겨주면 해당 인자가 테스트 이름에 포함된다.

{매개변수 인덱스} 말고도 다양한 템플릿을 사용할 수 있다.
{displayName} : 테스트 메서드 이름
{arguments} : 모든 인자를 쉼표로 구분해서 모두 표현
{argumentsWithNames} : 모든 인자를 이름과 함께 보여줌.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ParameterizedTest(name = "{0} vs {1} is {2}")
@CsvSource(value = {"BLACKJACK,BUST,WIN",
"BLACKJACK,STAND,WIN",
"STAND,BUST,WIN",
"BLACKJACK,BLACKJACK,DRAW",
"STAND,STAND,DRAW",
"BUST,BUST,DRAW",
"BUST,STAND,LOSE",
"STAND,BLACKJACK,LOSE",
})
void getResultFromStatus(Status status1, Status status2, Result expected) {
Result actual = Result.of(status1, status2);

assertThat(actual).isEqualTo(expected);
}

더 알아보기

https://www.baeldung.com/parameterized-tests-junit-5

https://www.arhohuttunen.com/junit-5-parameterized-tests/

Share