공부함
@ParameterizedTest를 사용해보자 본문
@ParameterizedTest를 사용하는 이유
@ParameterizedTest 는 테스트 메서드에 파라미터를 전달해 하나의 테스트 메서드로 여러 파라미터 묶음에 대해 테스트 할 수 있게 하는 기능이다. @ParameterizedTesf를 쓰면 좋은 점이 무엇일까?
중복 해소
100 종류의 상품에 대한 구매 테스트를 작성한다고 가정하자. @Test 를 사용한다면 100가지의 상품에 대해 전부 테스트 메서드를 작성해야 한다. @ParameterizedTest를 사용하면 상품 구매 테스트 메서드에 파라미터로 100종류의 상품을 전달해 하나의 테스트 코드 메서드를 재활용해 중복을 해소할 수 있다.
유지보수
구매 로직에 변경이 생긴다면 @Test로 작성한 100가지의 테스트 메서드를 전부 수정해야 한다. 이 과정에서 실수가 발생하기 쉽다. @ParameterizedTest로 작성한 테스트는 하나의 테스트 메서드만 수정하면 되기 때문에 유지보수성이 뛰어나다.
@MethodSource
@ParameterizedTest에 파라미터를 전달하는 방법은 여러가지가 있다.
@ParameterizedTest
@CsvSource({
"102812930, food, 5000, Shin Ramen, 농심, 20%, 2025-10-30, 중국, 쿠팡-곤지암, ~~~~",
"102812912, shoes, 100000, Nike Air Force, ~~~~~ 매우 많은 파라미터 ~~~"
})
void 상품_수정_테스트(String productId, String type, String price, String result) {
// 상품 수정
Product product = productRepository.findProductById(productId);
//
}
이렇게 @CsvSource를 통해 String 형태의 여러 파라미터를 전달할 수도 있다. 이외에도 @EnumSource, @ValueSource등이 있다. 이 글에서는 원하는 타입의 파라미터를 여러개 전달할 때 유용한 @MethodSource를 알아보자.
@ParameterizedTest
@MethodSource("provideParameters")
void 상품_등록_테스트(Product product, int count, String message) {
// 테스트 ..
}
private static Stream<Arguments> provideParameters() {
return Stream.of(
Arguments.of(
new Product(1000000L, "사과", 1000),
10,
"식료품 등록에 성공했습니다."
),
Arguments.of(
new Product(1000022L, "축구화", 10),
200,
"의류 등록에 성공했습니다."
),
Arguments.of(
new Product(1000033L, "후라이팬", 100000),
1,
"가전제품 등록에 성공했습니다."
)
);
@MethodSource의 속성값으로 파라미터를 제공할 메서드 이름을 넣는다. 파라미터 제공 메서드는 Stream<Arguments>형태로 파라미터를 반환하면 된다.
이렇게 하나의 테스트 메서드로 3가지 상품에 대해 테스트를 할 수 있다.
name 속성과 Named.named()
테스트 실행 시 UI에서 각 테스트에 대해 파라미터와 그 값을 순서대로 나열하고 있다. 객체인 Product는 주소값을 보여주고 있다. 한눈에 어떤 파라미터에 대한 테스트인지 파악하기 어렵다.
@ParameterizedTest(name = "[ {0} 상품 ] : [ {1} ] 개 등록 , 메세지 : [ {2} ]")
@MethodSource("provideParameters")
void 상품_등록_테스트(Product product, int count, String message) {
// 테스트 ..
}
private static Stream<Arguments> provideParameters() {
return Stream.of(
Arguments.of(
Named.named(
"사과",
new Product(1000000L, "사과", 1000)
),
10,
"식료품 등록에 성공했습니다."
),
Arguments.of(
Named.named(
"축구화",
new Product(1000022L, "축구화", 10)
),
200,
"의류 등록에 성공했습니다."
),
Arguments.of(
Named.named(
"후라이팬",
new Product(1000033L, "후라이팬", 100000)
),
1,
"가전제품 등록에 성공했습니다."
)
);
@ParameterizedTest의 name 속성과 Named.named() 메서드를 활용해 이름을 깔끔하게 표시할 수 있다. name 속성값으로 표시하고자 하는 이름을 넣는다. 이 때 파라미터 바인딩을 할 수 있다. 파라미터의 이름은 Named.named()의 첫번째 인자로 전달하고 두번째 인자로 파라미터를 전달한다.
훨씬 깔끔하게 표시되는 것을 확인할 수 있다.
활용예시
@ParameterizedTest는 다양하게 활용할 수 있다. @ParameterizedTest로 @Valid 검증 테스트 하는 예시를 확인해보자.
private static Stream<Arguments> provideInvalidInputs() {
return Stream.of(
Arguments.of("name", "", "이름은 필수입니다."),
Arguments.of("description", "길이제한을넘어가는텍스트", "설명은 최대 10자까지 가능합니다.")
);
}
@ParameterizedTest
@MethodSource("provideInvalidInputs")
void testValidation(String fieldName, Object invalidValue, String expectedErrorMessage) throws Exception {
// 기본 값으로 TestRequestDto 객체 생성
TestRequestDto requestDto = TestRequestDtoFixture.builder()
.name("정상 이름")
.description("정상 설명")
.build();
// 리플렉션을 사용하여 필드 값 변경
Field field = TestRequestDto.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(requestDto, invalidValue);
// DTO를 JSON 문자열로 변환
String requestBody = objectMapper.writeValueAsString(requestDto);
// MockMvc를 사용하여 POST 요청 보내기
mockMvc.perform(post("/my-endpoint")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors[0].field").value(fieldName))
.andExpect(jsonPath("$.errors[0].message").value(expectedErrorMessage));
}
요청 dto의 많은 필드를 스프링의 @Valid로 검증한다면 위와 같이 @ParameterizedTest를 활용해 한번에 테스트 해보자. 검증하려는 필드명을 파라미터로 받아 리플렉션을 활용해 잘못된 필드로 변경한다. mockMvc로 요청을 보내고 원하는 에러 응답을 얻는지 확인한다.
'테스트' 카테고리의 다른 글
테스트 더미데이터 삽입 방법 (2) | 2024.10.21 |
---|---|
테스트 환경 (@SpringBootTest와 @WebMvcTest) (1) | 2024.09.08 |