아래와 같이 javax.validation 어노테이션을 사용한 Dto 를 만들다가 문득 @NotBlank (Null과 Blank 값을 검증하는)와 @Size 를 중첩할 필요가 있을까? 하는 생각이 들었다.
생각해보니 빈값과 null 값은 당연히 다르니까 둘 다 써야겠다라는 결론이 나긴 했는데 Dto에 붙은 Validation 은 어떻게 단위테스트를 짤까하다찾아보게 되었다
게시글은 아래를 참고했다!
package com.refactoring.fcm.board.dto;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ArticleInsertRequestDto {
@NotBlank // @Size 만으로 검증 가능하지 않을까?
@Size(min = 1, max = 30, message = "제목이 너무 길거나 짧습니다!!")
private String subject;
@NotBlank
@Size(min = 1, max = 10000000, message = "내용이 너무 길거나 짧습니다!!")
private String content;
@Builder(builderMethodName = "createBuilder")
public ArticleInsertRequestDto(String subject, String content) {
this.subject = subject;
this.content = content;
}
}
테스트 한 Dto는 위와 같고, 테스트코드는 아래와 같다.
package com.refactoring.fcm.board.dto;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
import static org.junit.Assert.assertTrue;
class ArticleInsertRequestDtoTest {
private static Validator validator;
@BeforeAll
static void setUp() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@DisplayName("@Size @NotBlank")
@Test
void test() {
ArticleInsertRequestDto articleInsertRequestDto = ArticleInsertRequestDto.createBuilder()
.subject("") // @NotBlank, @Size에 모두 걸리는 상황
.content("")
.build();
Set<ConstraintViolation<ArticleInsertRequestDto>> violations = validator.validate(articleInsertRequestDto);
assertTrue(!violations.isEmpty()); // 비어있지 않음! -> 검증에 실패함
}
}
javax.validation 에서 제공하는 DefaultValidatorFactory 에서 제공하는 validator 를 통해 검증하는 코드인데,
일단 위 테스트 코드는 통과한다. 즉, violations 에 검증을 실패한 내용이 들어간다.
violations 에 뭐가 들어있나 디버깅해보니
@NotBlank 와 @Size 각각에 걸린 내용들이 들어가 있었다.
살펴보면,
@NotBlank 의 디폴트 메시지, @Size 의 메시지가 출력된다.
순서는 예상과 다르게 돌아간다. subject 필드 먼저 검사하고 content 검사할 줄 알았는데 .. 알파벳 순서인건가?
여기서 메시지 검증은 힘들지 않을까 생각했다
혹시나 @Size가 Null 체크도 해줄까 싶어 @NotBlank 를 주석처리하고 다음과 같이 테스트 해보았다.
@DisplayName("@Size로 Null 값 검증 되나?")
@Test
void testnull() {
ArticleInsertRequestDto articleInsertRequestDto = ArticleInsertRequestDto.createBuilder()
.subject(null)
.content(null)
.build();
Set<ConstraintViolation<ArticleInsertRequestDto>> violations = validator.validate(articleInsertRequestDto);
assertTrue(!violations.isEmpty());
}
아 안된다 ..!
초반에 말했다시피 Size 가 Null 체크 안하는건 당연한건데 @NotBlank, @Size 가 더덕더덕 붙어있는 느낌이라 뭐라도 빼고 싶었던 것 같다 ㅋㅋㅋㅋ
쨋든 에러메시지를 따로 정해두고 빈 값은 @Size 를 통해 검증하고 null 값은 @NotNull 로 검증해서 중복되지 않고 메시지를 따로 가져가는게 나을 것 같다고 자체적으로 결론을 내렸다
수정한 Dto 와 테스트는 아래와 같다.
package com.refactoring.fcm.board.dto;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ArticleInsertRequestDto {
@NotNull(message = "subject에는 null이 들어올 수 없습니다")
@Size(min = 1, max = 30, message = "subject은 1자이상 30자 이하여야합니다.")
private String subject;
@NotNull(message = "content에는 null이 들어올 수 없습니다")
@Size(min = 10, message = "content는 10자 이상이어야 합니다")
private String content;
@Builder(builderMethodName = "createBuilder")
public ArticleInsertRequestDto(String subject, String content) {
this.subject = subject;
this.content = content;
}
}
@DisplayName("@Size @NotNull 테스트")
@Test
void testSizeNotNull() {
ArticleInsertRequestDto articleInsertRequestDto = ArticleInsertRequestDto.createBuilder()
.subject(null)
.content("10자이하")
.build();
Set<ConstraintViolation<ArticleInsertRequestDto>> violations = validator.validate(articleInsertRequestDto);
List<ConstraintViolation> constraintViolations = violations.stream()
.collect(Collectors.toList());
assertThat(violations.size()).isEqualTo(2); // subject의 NotNull, content 의 Size
assertAll(
() -> assertThat(constraintViolations.get(0).getMessage()).isEqualTo("subject에는 null이 들어올 수 없습니다"),
() -> assertThat(constraintViolations.get(1).getMessage()).isEqualTo("content는 10자 이상이어야 합니다")
);
}
아래 메시지 체크는 불확실하다고 보인다.
나는 여러번 실행해본 결과 순서는 늘 같아서 한번 디버그 해보고 저렇게 체크했지만, 디버깅 하기 전에는 뭐가 먼저 나올지 모른다.
보통 저렇게 Validation 을 실패하면 Exception 을 던져주니까 윗단에서 Exception 테스트를 해도 될 것 같긴한데, 갑자기 Dto 단테도 함 짜볼까 싶었다 ㅎㅅㅎ
+ Java 게시글 너무 뜸하기도 했고 ,,
'개발 > Java & Spring ' 카테고리의 다른 글
[자바봄] producer - consumer 과제일기 : 1단계 (0) | 2020.04.13 |
---|---|
[기록] JUnit5 자주사용하는 코드 모음 (0) | 2020.04.01 |
[Java] Java Code Coverage(JaCoCo)를 사용해보자. (0) | 2019.12.20 |
[Spring boot - webpack] Webpack으로 js파일 모듈화하고 번들링하기 (0) | 2019.07.14 |
[Spring boot] 하나의 클래스로 properties 관리하여 사용하기! (0) | 2019.05.13 |