public class User {
@NotNull(message = "Name cannot be null")
@Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
private String name;
// Getters and setters
}
@Service
public class UserService {
@Autowired
private Validator validator;
public void validateUser(User user) {
Set<ConstraintViolation<User>> violations = validator.validate(user);
if (!violations.isEmpty()) {
for (ConstraintViolation<User> violation : violations) {
System.out.println(violation.getMessage());
}
}
}
}
validation을 사용하는 방법은 많지만 위와 같이 validation 기능을 사용했다고 하자
그럼 테스트 코드를 어떻게 작성해야할까?
[!NOTE]
(스프링에 익숙하지않은 사람들을 위해)
스프링 기능을 사용할려면 스프링 컨텍스트가 필요하다.
단지 new를 해서는 어노테이션 기반 스프링 기능이 동작하지 않을 수 있다는 사실만 생각하고 넘어가자.
public class UserServiceTest {
@Test
public void testValidateUser() {
// 'new'로 객체 생성
User user = new User();
user.setName(null); // 유효성 검사 실패 조건
UserService userService = new UserService();
userService.validateUser(user); // Validation 동작하지 않음
}
}
따라서 위 코드는 동작하지 않는다.
그럼 어떻게할까?
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Autowired
private Validator validator; // Spring에서 제공하는 Validator 빈 주입
@Test
public void testValidateUser() {
// User 객체 생성
User user = new User();
user.setName(null); // 유효성 검사 실패 조건
// Validation 수행
Set<ConstraintViolation<User>> violations = validator.validate(user);
// 검증 결과 확인
assertFalse(violations.isEmpty()); // 유효성 검사 실패해야 함
for (ConstraintViolation<User> violation : violations) {
System.out.println(violation.getMessage());
}
}
}
스프링 컨텍스트를 주입하면 된다.
문제점은, 스프링은 무겁다는 것이다.
내부적으로 리플렉션을 사용해 어노테이션들을 처리하는 방식을 채택하고 있기 때문에
컨텍스트를 전부 올리는것은 성능적으로 매우 큰 손해를 보는것이다.
이 문제를 가장 간단하게 해결하는 방법은 스프링 컨텍스트를 안쓰는 것이다.
어차피 지금 테스트하고싶은것은 검증이니까 ConstratintValidatator를 구현하면되지않을까?
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CustomNameValidator.class)
public @interface ValidName {
String message() default "이름이 유효하지 않습니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class CustomNameValidator implements ConstraintValidator<ValidName, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && value.length() >= 2 && value.length() <= 50;
}
}
public class CustomNameValidatorTest {
private final CustomNameValidator validator = new CustomNameValidator();
@Test
void testValidName() {
assertTrue(validator.isValid("홍길동", null));
}
@Test
void testInvalidNameTooShort() {
assertFalse(validator.isValid("홍", null));
}
@Test
void testNullName() {
assertFalse(validator.isValid(null, null));
}
}
new로만_객체_생성하면_스프링_검증기능_작동하지_않음
테스트 메서드를 살펴보면, 자카르타 validation 기능이 동작되지 않음을 알 수 있다.
만약 억지로 spring validation을 유지하면서 테스트를 하고 싶다면
직접_검증기_생성하면_유효성_수동_검사_가능
위 메서드와 같이 ConstraintViolation를 직접 명시해서 구현해야한다.

스프링 기능을 무조건 많이 쓴다고 테스트가 간편해지는 것은 아니다.
오히려 번거로워질 수도 있다.