Posted at

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


はじめに

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し、userFormbindingResultvalidateメソッドに渡せば、バリデーションを実行できます。バリデーション結果はbindingResultに入っているので、その中身に対しアサートをかければOKです。

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


補足

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

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