Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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でのテストパターンが減らせることは間違いないです。

lightcafe_gr
全国にグループ会社を持つIT企業です
https://www.lightcafe.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away