[JUnit 5] @ParameterizedTest 사용하기
by 배부른코딩로그테스트 조금 더 편하게 해보는건 어때?
최근 JUnit 5를 사용하면서 다양한 테스트 케이스에 대해 간편하게 하는 방법을 알게되어 기록을 남기게 되었다.
@ParameterizedTest 도입배경
만약, 신규 회원을 가입시킨다고 가정해보자.
사용자 비밀번호는 보안이 중요하기 때문에 강력한 보안정책을 필수적이다. 다양한 케이스에 대해서 비밀번호가 보안정책 규칙을 모두 따르는지 확인할 필요가 있다. 일단, 무식하게 각각의 규칙별로 테스트를 만들어봤다.
@Test
@DisplayName("Member 패스워드 8자리 미만 예외처리")
void createUserException01() {
assertThatThrownBy(() -> memberService.isValidPassword(INVALID_PASSWORD_UNDER_MIN))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
@DisplayName("Member 패스워드 16자리 초과 예외처리")
void createUserException02() {
assertThatThrownBy(() -> memberService.isValidPassword(INVALID_PASSWORD_UPPER_MAX))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
@DisplayName("Member 패스워드 특수문자 없는 경우 예외처리")
void createUserException03() {
assertThatThrownBy(() -> memberService.isValidPassword(INVALID_PASSWORD_NO_SPECIAL))
.isInstanceOf(IllegalArgumentException.class);
}
위 테스트는 isValidPassword라는 메서드를 대상으로 동일한 결과가 나오는 것을 테스트하는 부분이지만, 불필요하게 반복적으로 테스트가 진행되고 있다. 이를 가독성 좋고 더욱 명료하게 만들어주는 것이 @ParameterizedTest 애노테이션이다. 아래와 같이 깔끔하게 리팩토링이 가능하다.
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {"qwer", "qwerasdf1234", "qq232345678901234567!@"})
void itReturnsPasswordValidationException(String password){
assertThatThrownBy(() -> memberService.isValidPassword(password))
.isInstanceOf(PasswordValidationException.class);
}
개인적으로 애노테이션에 기입된 영문만 읽어도 이해되며, 가독성면에서도 월등하다고 생각된다. @ParameterizedTest에 대해서 조금 더 자세히 알아보자.
@ParameterizedTest 활용하기
이 애노테이션과 같이 사용할 수 있는 좋은 친구들이 있다. @ValueSource 및 @NullAndEmptySource(@NullSource, @EmptySource), @EnumSource, @MethodSource 등 다양한 친구들을 활용할 수 있다.
이름만 봐도 대충 어떤 상황에서 유용하게 쓰일지 감이 잡힌다. 이름 참 잘 만들었다 : )
@NullAndEmptySource
NULL 값과 "" 빈 문자열을 알아서 테스트해주는 애노테이션이다. 특정 필드 문자열에 대해 isEmpty 테스트가 필요하다면, 이 애노테이션 하나만으로 끝난다. 물론, @NullSource @EmptySource 각각 테스트도 가능하다.
@ValueSource
리터럴 값의 단일 배열을 지정할 수 있으며, 매개 변수화 된 테스트시 호출마다 단일 인수를 제공하는 데 사용할 수 있다. @ValueSource는 다음과 같은 타입의 리터럴 값을 지원한다.
- short, byte, int, long, float, double, char, boolean,
- java.lang.String, java.lang.Class
@ValueSource 안에 ints, strings 등 원하는 타입을 적어준 뒤, 리터럴 값을 나열하자.
@ValueSource(strings = {"qwer", "qwerasdf1234", "qq232345678901234567!@"})
@ValueSource(ints = {0, 1, 9999, -9999})
@EnumSource
enum도 당연히 지원한다.
@ParameterizedTest
@EnumSource(value = TimeUnit.class, names = {"DAYS", "HOURS"})
void testWithEnumSourceInclude(TimeUnit timeUnit) {
assertTrue(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}
@MethodSource
복잡한 인수를 테스트하는 위해 @MethodSource을 제공한다. 복잡한 오브젝트를 전달하는 것은 테스트에 대한 복잡도가 올라가거나 불가능하다.
@ParameterizedTest
@MethodSource("provideStringsForIsBlank")
void isBlank_ShouldReturnTrueForNullOrBlankStrings(String input, boolean expected) {
assertEquals(expected, Strings.isBlank(input));
}
private static Stream<Arguments> provideStringsForIsBlank() {
return Stream.of(
Arguments.of(null, true)
Arguments.of("", true),
Arguments.of(" ", true),
Arguments.of("not blank", false)
);
}
@MethodSource의 value에 메소드명을 적는다. Member와 같은 도메인 유효성 검사할 때 유용하게 활용이 가능하다.
@ParameterizedTest(name = "{index}: {3}")
@MethodSource("invalidParameters")
@DisplayName("Member 파라미터가 일치하지 않는 경우")
void invalidCreateString email, String password, String message, String exceptionMessage) {
assertThatThrownBy(() -> memberService.findOne(email, password))
.isInstanceOf(IllegalArgumentException.class);
}
static Stream<Arguments> invalidParameters() throws Throwable {
return Stream.of(
Arguments.of("", VALID_PASSWORD, "이메일 양식 오류", INVALID_EMAIL_MESSAGE),
Arguments.of("sample", VALID_PASSWORD, "이메일 양식 오류", INVALID_EMAIL_MESSAGE),
Arguments.of(VALID_EMAIL, "Passw0rd", "비밀번호 특수문자 누락", INVALID_PASSWORD_MESSAGE),
Arguments.of(VALID_EMAIL, "P@ssword", "비밀번호 숫자 누락", INVALID_PASSWORD_MESSAGE),
Arguments.of(VALID_EMAIL, "p@ssw0rd", "비밀번호 영대문자 누락", INVALID_PASSWORD_MESSAGE)
);
}
[출처]
(공식) JUnit 5 User Guide, https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests
HeeJeong Kwon, https://gmlwjd9405.github.io/2019/11/27/junit5-guide-parameterized-test.html
'TDD > JUnit' 카테고리의 다른 글
[JUnit 4] @FixedMethodOrder 통합 테스트 코드 작성하기 (0) | 2022.04.22 |
---|---|
[JUnit 4] @Nested 없이 계층 구조의 테스트 코드 작성하기 (0) | 2022.04.21 |
블로그의 정보
배부른코딩로그
배부른코딩로그