Spring カスタムvalidator単体テスト

# はじめに
* validatorにはmodelにアノテーションつけるだけで良くね的なチェックもありますが気にせずに。。。
* 近いうちにSpockで実装する記事を書く！。。。かも。

# サンプルvalidator
公式より拝借。ちょっとだけ変える。
[9.2 Validation using Spring’s Validator interface](http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#validator)

```java:PersonValidator.java
public class PersonValidator implements Validator {
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }

    public void validate(Object obj, Errors e) {
        Person p = (Person) obj;
        if (p.getAge() < 0) {
            e.rejectValue("age", "negativevalue");
        } else if (p.getAge() > 110) {
            e.rejectValue("age", "too.darn.old");
        }
        
        ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
        if(!e.hasErrror()) {
            if(p.getName.length() <= MAX_CHARS) {
                e.rejectValue("name", "error.entry.tooManyChars");
            }
        }
    }
}
```

# 実装
基底クラス。

```java:PersonValidatorTestBase.java
public class PersonValidatorTestBase {
    protected Person target;
    protected DataBinder binder;
    
    @Before
    public void 事前処理() {
        // target正常値初期化
        target = new Person();
        target.setAge(10);
        target.setName("valid name");
        
        // bind
        binder = new DataBinder(target);
        binder.setValidator(new PersonValidator());
    }
}
```
実装クラス。
ちょっと1つのテストが2つのメソッドに分かれちゃってるのがよくないかな。。まぁ雰囲気つかむのが目的ということで。

```java:PersonValidatorTest.java
@RunWith(Enclosed.class) //--(1)
public class PersonValidatorTest {
    @RunWith(Theories.class) //--(2)
	public static class 年齢テスト extends PersonValidatorTestBase {
        @DataPoints("ok") //--(3)
		public static MutablePropertyValues[] mpvArrOk = {
            new MutablePropertyValues()
                .add("age" , 0)
                .add("description" , "年齢のOK境界値MIN"),
            new MutablePropertyValues()
                .add("age" , 110)
                .add("description" , "年齢のOK境界値MAX")
        };
        
        @DataPoints("ng")
		public static MutablePropertyValues[] mpvArrNg = {
            new MutablePropertyValues()
                .add("age" , -1)
                .add("description" , "年齢がマイナステスト")
			    .add("errCode"     , "negativevalue"),
            new MutablePropertyValues()
                .add("age" , 111)
                .add("description" , "年齢行き過ぎテスト")
			    .add("errCode"     , "too.darn.old")
        };
        
        @Theory //--(4)
		public void アプリ相関チェック_正常系(@FromDataPoints("ok") MutablePropertyValues mpv) {
            binder.bind(mpv); //--(5)
			binder.validate();
            
            BindingResult br = binder.getBindingResult();
			assertThat(br.hasErrors())
                .as((String)mpv.get("description"))
                .isFalse();
        }
        
        @Theory
		public void アプリ相関チェック_異常系(@FromDataPoints("ng") MutablePropertyValues mpv) {
			binder.bind(mpv);
			binder.validate();
			
            // descriptionとerrCodeはちょっと強引
			BindingResult br = binder.getBindingResult();
			assertThat(br.getFieldError("age").getCode())
				.as((String)mpv.get("description"))
				.isEqualTo((String)mpv.get("errCode"));
		}
    }
    
    @RunWith(Theories.class)
	public static class 名前テスト extends PersonValidatorTestBase {
        // 省略
    }
}
```
### (1) Enclosed.class
* テスト項目ごとに階層を分けるためのランナー

### (2) Theories.class
* DataPointsなどを使用するためのランナー

### (3) @DataPoints(名前)
* ここで指定した配列分だけテストが繰り返される
* テスト対象の「フィールド名, 値」でaddしていく
* フィールドとして定義されていないものもaddできる   
※今回はdescriptionとadd。これに関してはもっといいやり方知りたい。

### (4) @Theory
* DataPoints使うためのアノテーション。
* 引数にDataPointsとして定義した型の変数を指定

### (5) DataBinder.bind
* bindによってテスト対象のモデルに値が代入される

# 参考
[JUnit入門その9[@RunWithアノテーションを利用したテストの作成]](http://javaworld.helpfulness.jp/post-81/)