はじめに
Form
クラスなどの単項目バリデーションの動作確認を、Controller
のテストで書いているのを見かけることがあります。Controller
のテストは事前に書くことが多く、テストパターンが多いとテストクラスが大きくなって可読性も悪くなります。
Form
クラスのテストとして書けばコード量も減り、テスト内容も明確にすることができます。
以下、Spring Boot 2.0.5 で確認。
テスト対象クラス
以下のUserForm
クラスのバリデーションをテストします。
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
クラスに対するテストクラスとして作成。
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
でのテストパターンが減らせることは間違いないです。