Spring 単項目バリデーションのテスト

# はじめに
`Form`クラスなどの単項目バリデーションの動作確認を、`Controller`のテストで書いているのを見かけることがあります。`Controller`のテストは事前に書くことが多く、テストパターンが多いとテストクラスが大きくなって可読性も悪くなります。

`Form`クラスのテストとして書けばコード量も減り、テスト内容も明確にすることができます。

以下、Spring Boot 2.0.5 で確認。

# テスト対象クラス
以下の`UserForm`クラスのバリデーションをテストします。

```java
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Data
public class UserForm {

    @NotNull
    private Integer userId;

    @Size(max = 2)
    @NotBlank
    private String userName;

}
```

**バリデーション**
各フィールドにアノテーションで以下のバリデーションを実装。

- `userId`
    - `@NotNull`
        - `null`を許可しない。
- `userName`
    - `@Size(max = 2)`
        - 最大文字数は2。
    - `@NotBlank`
        - `null`、空文字、半角スペースのみ入力を許可しない。

# テストクラス
`UserForm`クラスに対するテストクラスとして作成。

```java
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;

import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class UserFormTest {

    @Autowired
    Validator validator;

    private UserForm userForm = new UserForm();
    private BindingResult bindingResult = new BindException(userForm, "UserForm");

    @Before
    public void before() {
        userForm.setUserId(1);
        userForm.setUserName("劉備");
    }

    /**
     * エラーなし
     */
    @Test
    public void noError() {
        validator.validate(userForm, bindingResult);
        assertThat(bindingResult.getFieldError(), is(nullValue()));
    }

    /**
     * userIdがnull
     */
    @Test
    public void userIdIsNull() {
        userForm.setUserId(null);
        validator.validate(userForm, bindingResult);
        assertThat(bindingResult.getFieldError().getField(), is("userId"));
        assertThat(bindingResult.getFieldError().getDefaultMessage(), is("must not be null"));
    }

    /**
     * userNameのサイズが上限超え
     */
    @Test
    public void userNameSizeIsOverLimit() {
        userForm.setUserName("夏侯惇");
        validator.validate(userForm, bindingResult);
        assertThat(bindingResult.getFieldError().getField(), is("userName"));
        assertThat(bindingResult.getFieldError().getDefaultMessage(), is("size must be between 0 and 2"));
    }

    /**
     * userNameがnull
     */
    @Test
    public void userNameIsNull() {
        userForm.setUserName(null);
        validator.validate(userForm, bindingResult);
        assertThat(bindingResult.getFieldError().getField(), is("userName"));
        assertThat(bindingResult.getFieldError().getDefaultMessage(), is("must not be blank"));
    }

    /**
     * userNameが空文字
     */
    @Test
    public void userNameIsBlank() {
        userForm.setUserName("");
        validator.validate(userForm, bindingResult);
        assertThat(bindingResult.getFieldError().getField(), is("userName"));
        assertThat(bindingResult.getFieldError().getDefaultMessage(), is("must not be blank"));
    }

    /**
     * userNameが半角スペースのみ
     */
    @Test
    public void userNameIsOnlySpace() {
        userForm.setUserName("  ");
        validator.validate(userForm, bindingResult);
        assertThat(bindingResult.getFieldError().getField(), is("userName"));
        assertThat(bindingResult.getFieldError().getDefaultMessage(), is("must not be blank"));
    }
}
```

`org.springframework.validation.Validator`を`@Autowired`し、`userForm`と`bindingResult`を`validate`メソッドに渡せば、バリデーションを実行できます。バリデーション結果は`bindingResult`に入っているので、その中身に対しアサートをかければOKです。

テストメソッドの書き方としては、`before`で正常系の状態を作っておき、各テストメソッドで一部だけ異常系に変更してテストするのが、見た目的にもスッキリしていいかなと思います。

# 補足
`Form`クラスで単項バリデーションテストできるから`Controller`のテストは全くいらないかと言われるとそうではなく、例えば`Controller`でエラーメッセージの変換をしているとか、そもそも`Form`クラスにバリデーションがかかっているのか確認したいとかいう時は`Controller`でテストする必要があります。

ただ網羅的なテストは`Form`クラスのテストで抑えられるので、`Controller`でのテストパターンが減らせることは間違いないです。