오늘은 JUnit5 의 @ParameterizedTest
에 대해서 알아볼 것이다.
이를 위해 사용되는 필요 기술은 다음과 같다.
- SpringBoot 2.
- JUnit 5
- Validation
- ControllerAdvice
이와 같은 DTO 객체가 존재한다고 가정해보자.
@NotBlank
나 @Size
나 @Email
은 모두 validation 에서 사용하는 것인데, 만약 이 입력 값들 중에서 해당 constraint를 만족시키지 못한다면 MethodArgumentNotValidException
을 발생시킨다.
그럼 ControllerAdvice를 이용해서 해당 예외가 발생하였을 때 ErrorResponse를 응답하는 구조로 만들어보자.
ControllerErrorAdvice.class
@ResponseBody
@ControllerAdvice
public class ControllerErrorAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
return new ErrorResponse(exception.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}
}
해당 ControllerAdvice 에서 @ExceptionHandler
로 어떤 예외가 발생하였는지를 잡아서 @ResponseStatus
에 우리가 내려줄 상태 코드를 명시해준다.
DTO
public class ErrorResponse {
private String message;
public ErrorResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
해당 예외에 지정될 message 필드를 받아 클라이언트에게 전달해줄 DTO 객체이다.
ControllerTest
@SpringBootTest
class ControllerTest {
@Autowired
private MemberService memberService;
@ParameterizedTest // 1
@DisplayName("회원 가입 - 비정상적 입력")
@MethodSource("paramsForSignUpInvalidInputs") // 2
void signUp_with_invalid_inputs(String email, String password, String nickname) throws Exception{
// given
Member member = Member.builder()
.email(email)
.password(password)
.nickname(nickname)
.build();
// when & then
MemberRequestSignUpData memberRequestData = modelMapper.map(member, MemberRequestSignUpData.class);
mockMvc.perform(post("/api/members")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(memberRequestData)))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("message").exists());
}
private static Stream<Arguments> paramsForSignUpInvalidInputs() { // 3
return Stream.of(
Arguments.of("noEmail", "test1234", "James"), // 이메일 형식이 아님
Arguments.of("dhslrl321@gmail.com", "noPw", "James"), // 비밀번호가 8글자 미만
Arguments.of("dhslrl321@gmail.com", "test1234", "1"), // 닉네임이 2글자 미만
Arguments.of("", "", "") // 값이 비어있는 경우
);
}
}
이와 같은 형태로 사용된다.
아직 무슨 내용인지 몰라도 괜찮다.
하나 하나 알아보자.
주석 1. @ParameterizedTest
ParameterziedTest 는 JUnit 4에서 사용되었던 @RunWith(Parameterized.class)
와 동일한 역할을 수행한다.
테스트 메서드 하나를 이용해서 다양한 매개변수를 이용한 테스트 결과를 제공하는데,
JUnit 공식 문서에서 @ParameterizedTest
는 다음과 같이 표현된다.
@ArgumentsSource
나 해당 주석에 대응하는 필드를 이용해서 전달할 매개 변수를 지정해야 하는데, 해당 어노테이션을 사용하는 테스트 메서드는 private 이나 static 이어서는 안된다.
다양한 Source를 이용해서 파라미터를 넘길 수 있다.
다양한 Source
@ValueSource
: string, int 형식의 primtive 타입을 전달할 수 있다.@ValueSource(strings = {"a", "b", "c"})
@NullSource
: Null 값을 파라미터로 넣어준다.@EmptySource
:@EnumSource
: Enum 타입의 value를 파라미터로 넣어준다.@EnumSource(value = UserRole.class, name = {"ADMIN", "USER"}
@MethodSource
- 가장 중요한 Source로 더 자세히 알아보자.
주석 2. @MethodSource
해당 어노테이션이 가장 중요하다는 데에는 이유가 있다.
위에서 보았던 방식의 어노테이션은 하나의 타입만을 파라미터로 넘길 수 있는데, @MethodSource
를 사용하면 여러 타입을 넘기는 것이 가능해진다.
이게 왜 중요한걸까?
우리가 테스트할 대상은 하나의 타입만으로 이루어져있지 않고 복잡한 타입으로 구성되어있을 가능성이 매우 크기 때문이다.
앞서 보았던 테스트 메서드에 조금 변형을 시켜보자.
- email: String
- age: int
- UserType: UserType.Enum
- nickname: String
이라고 한다면 벌써 3개의 타입이 존재한다.
이런 경우 @MethodSource
를 사용할 수 있는 것이다.
@MethodSource는 메서드에서 반환되는 스트림을 이용해서 테스트의 파라미터로 넘기는데, 파라미터로 전달될 스트림을 반환하는 메서드를 작성해주면 된다.
아래서 더 자세히 알아보자.
주석 3. paramsForSignUpInvalidInputs()
private static Stream<Arguments> paramsForSignUpInvalidInputs()
의 형태를 보면 알겠지만 해당 메서드에서 주목해야할 점이 2개가 존재한다.
- static
- Stream
앞서 말 했듯 MethodSource는 스트림을 반환해야 한다고 했다.
우리는 arguments 제너릭을 이용한 스트림을 반환할 것이고 해당 어노테이션은 꼭 static 메서드여야 한다.
테스트 코드 돌려보기
위와 같은 메서드가 넘기는 Stream으로 테스트 코드를 돌려보자.
그럼 정상적으로 우리의 기능이 잘 동작하는 것을 볼 수 있다.
'💊 Java & Kotlin & Spring > - Spring' 카테고리의 다른 글
[Spring Boot] 터미널에서 빌드 툴 (Maven, Gradle)로 Spring Boot 실행시키기 (0) | 2021.04.24 |
---|---|
[Spring Boot] JUnit5 BDDMockito로 알아보는 TDD와 BDD 의 차이 및 BDD 실습 (0) | 2021.04.22 |
[Spring Framework] Spring-Core(BeanFactory 인터페이스) 분석 및 구현 (0) | 2020.09.07 |
[Spring Framework] AOP에 대한 용어들과 기본 컨셉에 대해서 알아보자. (0) | 2020.09.05 |
[Spring Framework] DI Application을 만들어보자. (0) | 2020.09.05 |
댓글0