Spring MVC Validation Testing

참조 :
Spring MVC 9장
springmodules의 valang을 활용한 Validation 테스트
Where is ErrorsVerifier class mentioned in Expert Spring MVC?

본문에서 Validator를 테스트 하는 방법을 소개하고 있습니다.

public class PersonValidatorTests extends TestCase {
    public void testEmptyPersonValidation() {
        Person person = new Person();
        Validator validator = new PersonValidator();
        BindException errors = new BindException(person, “target”);
        validator.validate(person, errors);
        new ErrorsVerifier(errors) {
            {
            forProperty(“firstName”).hasErrorCode(“person.firstName.required”)
            .forProperty(“lastName”).hasErrorCode(“person.lastName.required”)
            .otherwise().noErrors();
            }
        }
    }
}

위의 소스 코드는 Spring MVC 281페이지 그대로 입니다. 하지만 위의 코드는 선뜻 이해가 가지 않았습니다.
언뜻 봤을 때 person 객체를 두번이나 사용한 것부터 뭔가 이상하게 느껴졌으며(좀 더 보고 있으니까 바인딩 할 때 한번 검증할 때 한 번 사용하는 의도 인것 같습니다.)
ErrorsVerifier라는 익명 클레스의 사용이 낯설었습니다.

ErrorsVerifier를 사용한 것을 보면 fluent interface 개념으로 구현하여 매우 재밌고 직관적으로 작명된 메소드의 이름들을 확인할 수 있습니다. 하지만 안타깝게도 책에 나온 설명과 달리 저 클레스(?)는 spring 프레임웤에 존재하지 않습니다.

이 클레스를 찾느라 여러명이 고생한 흔적을 구글에서 찾을 수 있었는데 그 곳에서 보다 더 값진 것을 발견했습니다. 바로 뛰어난 선배님재미난 참여를 살펴 볼 수 있었습니다.

위 링크에 가시면 Spring에는 전혀 없던 ErrorsVerifier 코드를 만들어 낸 과정이 들어있습니다.

ErrorsValidator 다운dm231.java

ErrorsValidator를 사용한 예제 코드

public class MemberInfoValidatorTest extends TestCase{

    public void testEmptyMemberInfo() {
        MemberInfo memberInfo = new MemberInfo();
        Validator validator = new MemberInfoValidator();
        BindException errors = new BindException(memberInfo, “target”);
        validator.validate(memberInfo, errors);
        ErrorsVerifier errorsVerifier = new ErrorsVerifier(errors);
        errorsVerifier.forProperty(“name”).hasErrorCode(“required”)
            .forProperty(“password”).hasErrorCode(“required”)
            .forProperty(“confirmPassword”).hasErrorCode(“required”)
            .forProperty(“email”).hasErrorCode(“required”);
    }
}

음~ 좋네요. 다시 보니까 Spring MVC 에 나온 코드는 컴파일 에러가 나지 않을까 싶네요;; 클레스파일이 없기도 없거니와 인너 클래스의 사용이 이상해요. 중괄호 연속 두 개씩.. 대체 무슨 의미인건지;;; 어떻게 하라는건지~ㅋㅋ

Spring MVC Validation 정리

Programmatic
Vlidator – ValidationUtils 사용하기
Vlidator – Property 파일 사용하기

Declarative
Declarative Validators – Valang 사용하기
    – Valang – syntex
    – Valang – CustomFunction
    – ValangValidatorFactoryBean -> ValangValidator

참조
Fail-fast vs. complete validation
Really easy field validation 사용하기 (4)
Spring MVC 9장
Spring Modules

ValangValidatorFactoryBean -> ValangValidator

링크 : Spring Modules Wiki ::
Using Valang validator

Spring Modules 사이트는 https 라 그런가 약간 느리고 답답합니다. Spring Modules API를 보던 중에 Spring MVC 9장에서 예제로 사용하는 ValangValidatorFactoryBean이 보이질 않았습니다. 이게 대체 무슨일인가… 하고 구글링을 했더니 Spring Modules 0.4 이전에 사용하던 클레스인데 0.5 부터는 ValangValidator로 대체 된 것(deprecated 된 후 삭제) 같습니다.

Prior to release 0.4, the
org.springmodules.validation.ValangValidatorFactoryBean could be used
as an alternative to org.springmodules.validation.ValangValidator.
Since both classes served the same purpose, the former was deprecated
and is planned to be removed in 0.5.

이름이 짧아져서 좋습니다. 🙂

둘의 차이점으로는 ValangValidatorFactoryBean에서는 syntax라는 property로 valang을 등록했는데 ValangValidator에서는 valang이라는 property에 설정한다는 것입니다. 보며 좋은 이름을 선택했다고 볼 수 있습니다.

그 외에 customFuction을 등록할 수 있는 것은 같습니다.

Valang – CustomFunction

Valang이 제공하는 여러 Validation로직이 있지만 사용자가 직접 특정 로직을 구현하여 끼워넣을 수 있습니다.

AbstractFunction 을 상속하여 구현합니다.

제약사항으로 세 개의 인자(Functions[] functions, int line, int column)를 가지는 생성자를 만들어서 AbstractFunction 클레스를 생성할 때 사용할 수 있도록 super를 호출해야 합니다.

Spring MVC 9장에 나와있는 예제로 그다지 쓸만해 보이지는 않지만 입력받은 값의 case를 뒤바꿔서 검증을 하는 예제가 나옵니다.



import org.apache.commons.lang.WordUtils;


import org.springmodules.validation.valang.functions.AbstractFunction;


import org.springmodules.validation.valang.functions.Function;


 



public class AlterCaseFunction extends AbstractFunction {

       public AlterCaseFunction(Function[] functions, int line, int column) {


             super(functions, line, column);


             definedMinNumberOfArguments(1);


             definedMaxNumberOfArguments(1);


       }


 


       protected Object doGetResult(Object target) {


             String value = getArguments()[0].getResult(target).toString();


             return WordUtils.swapCase(value);


       }


}


이렇게 작성한 Function을 ValangValidatorFactoryBean에 등록하여 syntax에서 사용할 수 있습니다.



<bean id=“caseSwappingValidator”


       class=“org.springmodules.validation.ValangValidatorFactoryBean”>


       <property name=“syntax”>


             <value>


                    <![CDATA[


{ name : alterCase(?) = ‘sTEVEN’ : ‘Name must be Steven’ }


]]>


             </value>


       </property>


       <property name=“customFunctions”>


             <map>


                    <entry key=“alterCase”


       value=“com.apress.expertspringmvc.validation.AlterCaseFunction” />


             </map>


       </property>


</bean>


흠… 그런데..API에서 ValangValidatorFactoryBean 이녀석이 사라졌습니다. 어디로 갔을런지;;

Valang – syntex

기본 적인 syntex 예제 입니다.

{ location : ? is not blank : ‘Location must be specified’ : ‘addressLocationEmpty’ : ? }

1. validation syntex는 {와 } 사이에 들어가야 합니다.

2. 인자는 총 다섯 개가 올 수 있으며 : 로 구분합니다. 마지막 인자는 생략 가능합니다.
– 첫 번째 인자 : 검사할 객체의 프로퍼티
– 두 번째 인자 : 검사하는 로직
– 세 번째 인자 : default message
– 네 번째 인자 : 프로퍼티 파일의 에러코드
– 다섯 번째 인자 : 아규먼트

3. 프로퍼티를 가져오는 방법
– name
– address.location
– customers[0].name
– salesParameters[seasonStartDate]
위와 같이 다양한 벙법으로 가져 올 수 있으며 기본적으로 String 타입으로 가져옵니다. 이 때 []를 사용하면 그 안의 값은 Date 타입으로 인식합니다. 현재 시각을 나타내는 T 를 사용할 수 있습니다.
예) 입력한 시각이 현재 시각보다 앞서지 않도록 검증
{ order.timestamp : ? < [T] : ” : ‘order.timestamp.in_past’ }
예) 입력한 시각이 오늘이라는 시간대에 포함되는지 검증
{ order.timestamp : ? between [T<d] and [T>d] : ” : ‘order.timestamp.not_today’ : ? }

4. 로직을 작성할 때 사용할 수 있는 오퍼레이터들
– String 관련 오퍼레이터
사용자 삽입 이미지사용자 삽입 이미지– Date 관련 오퍼레이터
사용자 삽입 이미지– Date Incremental operator
사용자 삽입 이미지– Date Regural Expression
사용자 삽입 이미지