[JUnit] @ParameterizedTest, @MethodSource으로 한번에 테스트하자
Introduction
REST API 개발 공부를 하면서 TDD 사용하는 방식이 궁금하여 요즘 인프런의 백기선님 강의로 학습하고 있다. 해당 강의에서는 JUnit4를 사용하고 있고, 본인은 JUnit5로 학습하고 있다. 매개변수를 사용하는 테스트 코드를 작성하던 중 버전별로 지원하는 방식이 다르다는 것을 알게되었고, @ParameterizedTest로 사용하는 방식이 유용하고 편리하여 사용방법에 대해 정리하고자 한다.
먼저, 아래의 user 클래스는 비밀번호 유효성검사를 간단하게 확인하는 메소드이다. 이 메소드를 사용하여 테스트 코드에서는 비밀번호가 유효한지 확인할 것이다.
@Builder
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class User {
@Id @GeneratedValue
private Integer id;
private String password;
private boolean match;
public boolean isValidPassword(String password) {
// 대문자가 없는지 확인
if (!password.chars().anyMatch(Character::isUpperCase)) {
return false;
}
// 소문자가 없는지 확인
if (!password.chars().anyMatch(Character::isLowerCase)) {
return false;
}
// 숫자가 없는지 확인
if (!password.chars().anyMatch(Character::isDigit)) {
return false;
}
// 특수문자가 없는지 확인
if (!password.chars().anyMatch(ch -> "!@#$%^&*".indexOf(ch) >= 0)) {
return false;
}
return true;
}
}
@ParameterizedTest 란?
다른 인자들을 사용해 테스트를 여러번 할 수 있다.
테스트를 하면 주입시키는 값에 따라 다른 결과들이 나온다. 해당 값에 맞춰 각각 작성하면 만드는 것도, 관리하는 것도, 가독성에도 좋지 않다.
그래서 인자로 테스트할 값을 Source로 만들어 주입시킨다. 이를 사용하면 여러개의 테스트를 하나의 포맷으로 사용할 수 있다.
비밀번호를 무작위로 생성하여 유효성 검사를 하는 코드이다. 다양한 조건의 인자 값으로 인해 중복되는 코드가 작성되었다.
@Test
@DisplayName("대소문자,숫자,특수문자가 모두 포함하는지 확인하는 테스트")
void testValidPassword() {
assertThat("Abcdefg1!").isEqualTo(PASSWORD_MATCH_MESSAGE);
}
@Test
@DisplayName("특수문자와 숫자가 미포함하는지 확인하는 테스트")
void testPasswordMissingNumberAndSpecialChar() {
assertThat("Abcdefg!").isEqualTo(PASSWORD_NOT_MATCH_MESSAGE);
}
@Test
@DisplayName("대문자만 미포함하는지 확인하는 테스트")
void testPasswordMissingUppercase() {
assertThat("abcdefg1!").isEqualTo(PASSWORD_NOT_MATCH_MESSAGE);
}
@Test
@DisplayName("소문자만 미포함하는지 확인하는 테스트")
void testPasswordMissingLowercase() {
assertThat("ABCDEFG1!").isEqualTo(PASSWORD_NOT_MATCH_MESSAGE);
}
@Test
@DisplayName("숫자만 미포함하는지 확인하는 테스트")
void testPasswordMissingDigit() {
assertThat("Abcdefg!").isEqualTo(PASSWORD_NOT_MATCH_MESSAGE);
}
@MethodSource
@MethodSource를 사용하여 많은 인수들을 한꺼번에 파라미터로 넘겨줄 수 있다.
@ParameterizedTest(name = "password : {0} => 결과 : {1}")
@MethodSource("paramsForTestPassword")
@DisplayName("입력된 비밀번호가 유효한지 검사하는 테스트 입니다")
public void testPasswordValidation(String password, boolean isMatch) {
// Given
User user = User.builder()
.password(password)
.build();
// When
user.isValidPassword(password);
// Then
assertThat(password).isEqualTo(isMatch);
}
static Stream<Arguments> paramsForTestPassword() {
return Stream.of(
Arguments.arguments("Abcdefg1!",true),
Arguments.arguments("Abcdefg",false),
Arguments.arguments("abcdefg1!",false),
Arguments.arguments("ABCDEFG1!",false),
Arguments.arguments("Abcdefg!",false)
);
}
paramsForTestPassword() 메소드를 정의한 후, 이 메소드를 @MethodSource를 이용하여 테스트 메소드로 넘겨준다.
기본 규칙은 아래와 같다.
- @MethodSource에 작성하는 메소드 이름은 인수로 제공하려는 메소드 이름과 동일해야 한다.
- 인수로 제공하는 메소드는 static이어야 한다.
- 단, @TestInstance(Lifecycle.PER_CLASS)를 사용하여 클래스 단위 생성주기이면 인스턴스 메소드 제공이 가능하다.
- @MethodSource에 메소드 이름을 작성하지 않으면 JUnit이 테스트 메소드 이름과 동일한 메소드를 찾아서 인수로 제공한다.
- 만약 테스트 호출 당 하나의 인수만 제공하고자 한다면 Arguments로 추상화 할 필요가 없다.
- argument source method는 Stream<Arguments>를 반환하는 것이 기본이지만, List와 같은 컬렉션, 인터페이스를 반환할 수 있다.
- 정규화된 이름(FQN#methodName)으로 외부 정적 메소드를 참조할 수 있다.
위의 방식을 사용하여 기존 반복되는 코드를 줄일 수 있게 되었다.
Reference
https://www.baeldung.com/parameterized-tests-junit-5
Guide to JUnit 5 Parameterized Tests | Baeldung
Learn how to simplify test coverage in JUnit 5 with parameterized tests
www.baeldung.com
https://zzang9ha.tistory.com/344#parameterizedtest,-methodsource
JUnit - @ParameterizedTest, @ValueSource, @CsvSource, @MethodSource 어노테이션
• 안녕하세요~ 이전에 운영하던 블로그 및 GitHub, 공부 내용을 정리하는 Study-GitHub 가 있습니다! • 네이버 블로그 • GitHub • Study-GitHub • 🐔 📎 JUnit5 - @ParameterizedTest, @ValueSource, @CsvSource, @MethodS
zzang9ha.tistory.com