1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SpringBoot Validation(単体テストあり)

Last updated at Posted at 2022-06-26

概要

SpringBootの入力欄にバリデーションを追加していきます。
テストコードも書きます。

テストコードを書く上で工夫したこと

プロダクトコードにおいてバリデーションの責務はモデルにまとめさせる。
コントローラーでの処理はモデルのメソッド呼んで処理する。
コントローラーにはなるべく書かないようにする

動作環境

OS: Windows10
IDE: IntelliJCommunity
SpringBoot version 2.7.0
Java:17

動かし方

1.以下URLからリポジトリをクローン
https://github.com/RYA234/spring_boot_memo
branchは「validation」を選択

2.src/main/java/com/example/spring_boot_memo/SpringBootMemoApplicationを実行します。
image.png

3.以下URLを開きます
http://localhost:5000/
開くと以下画面が表示されます。
image.png

内容

画面

不適切な値を入れるとメッセージ出します。至ってシンプルな構造ですね
image.png

image.png

プロダクトコード

model

ModelStaff.java
package com.example.spring_boot_memo.validation;

import lombok.Data;
import org.jetbrains.annotations.MustBeInvokedByOverriders;
import org.springframework.validation.BindingResult;

import javax.validation.constraints.*;

@Data
public class ModelStaff {
    @Min(value = 0)
    @Max(value = 100)
    public Integer id;

      @NotEmpty(message="スタッフ名を入力してください")
      @Size(max = 15, message = "スタッフの名前は15字以内で入力してください")
      String name;

 BindingResult bindingResult;
}

view

main.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Validation</title>

</head>
<body>
thymeleaf

    <form method="post" action="/validation/check"  th:object="${modelStaff}"  >
        <input type ="number" th:field="*{id}"  th:errorclass="is-invalid">
        <p class="invalid-feedback" th:errors="*{id}">Id Error</p>
        </br>  </br>
        <input type ="text" th:field="*{name}"  th:errorclass="is-invalid">
        <p class="invalid-feedback" th:errors="*{name}">Name Error</p>
        </br>  </br>
        <button type="submit" name="OK">
            バリデーションショック
        </button>

    </form>

</body>
</html>

Controller

MainController.java
package com.example.spring_boot_memo.validation;


import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.io.IOException;

@org.springframework.stereotype.Controller
public class MainController {

    @RequestMapping(value = "index", method = RequestMethod.GET)
    public String indexController(Model model,ModelStaff modelStaff)
    {
        System.out.println("aaa");

        return "index";
    }

    @RequestMapping(value = "validation/main", method = RequestMethod.GET)
    public String validationController(Model model)
    {
        System.out.println("aaa");
        ModelStaff modelStaff = new ModelStaff();
        model.addAttribute("modelStaff",modelStaff);

        return "validation/main";
    }


    @RequestMapping(value = "validation/check",params ="OK", method = RequestMethod.POST)
    public String validationCheckController(Model model, @Validated ModelStaff modelStaff, BindingResult bindingResult) throws IOException
    {


        System.out.println(modelStaff);
        if(bindingResult.hasErrors())
        {
            model.addAttribute("modelStaff",modelStaff);
            return "validation/main";
        }

        return "validation/check";
    }

}

テストコード

テストコード書くために、Mockito と MockMvcを使っています。

ValidationTest.java
package com.example.spring_boot_memo;


import com.example.spring_boot_memo.validation.MainController;
import com.example.spring_boot_memo.validation.ValiService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;

@AutoConfigureMockMvc
@SpringBootTest
public class ValidationTest {

    @Autowired
    MockMvc mockMvc;


    @Mock
    ValiService valiService;
    @InjectMocks
    MainController mainController;

    @BeforeEach
    void setup()
    {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(mainController).build();
    }

    @Test
    @DisplayName("1.name=ASBA id=11 結果:OK")
     public void Case1() throws Exception{
       this.mockMvc.perform(post("/validation/check")
                .param("OK","OK")
                       .param("name","ASBA")
                       .param("id","11")
               )
               .andDo(print())
               .andExpect(model().hasNoErrors()
                       );
    }

    @Test
    @DisplayName("2.name=ASBA id=101 結果:NG")
    public void Case2() throws Exception{
        this.mockMvc.perform(post("/validation/check")
                        .param("OK","OK")
                        //   .param("name","dsada"))
                        .param("name","ASBA")
                        .param("id","101")
                )
                .andDo(print())
                .andExpect(model().hasErrors()
                );
    }

    @Test
    @DisplayName("3.name=ASBA id=100 結果:OK")
    public void Case3() throws Exception{
        this.mockMvc.perform(post("/validation/check")
                        .param("OK","OK")
                        .param("name","ASBA")
                        .param("id","100")
                )
                .andDo(print())
                .andExpect(model().hasNoErrors()
                );
    }

    @Test
    @DisplayName("4.name=ASBA id=100 結果:OK")
    public void Case4() throws Exception{
        this.mockMvc.perform(post("/validation/check")
                        .param("OK","OK")
                        .param("name","ASBA")
                        .param("id","100")
                )
                .andDo(print())
                .andExpect(model().hasNoErrors()
                );
    }

    @Test
    @DisplayName("5.name=ASBA id=0 結果:OK")
    public void Case5() throws Exception{
        this.mockMvc.perform(post("/validation/check")
                        .param("OK","OK")
                        .param("name","ASBA")
                        .param("id","0")
                )
                .andDo(print())
                .andExpect(model().hasNoErrors()
                );
    }

    @Test
    @DisplayName("6.name=null id=50 結果:NG")
    public void Case6() throws Exception{
        this.mockMvc.perform(post("/validation/check")
                        .param("OK","OK")
                        .param("name","")
                        .param("id","50")
                )
                .andDo(print())
                .andExpect(model().hasErrors()
                );
    }

    @Test
    @DisplayName("7.name=ASBA id=-1 結果:NG")
    public void Case7() throws Exception{
        this.mockMvc.perform(post("/validation/check")
                        .param("OK","OK")
                        .param("name","ASBA")
                        .param("id","-1")
                )
                .andDo(print())
                .andExpect(model().hasErrors()
                );
    }

}


実行結果
image.png

感想

アノテーション付ければModelにValidationの責務つけるのは楽でした。

処理がview-Controller間であるためそこの関係を理解するのに苦労しました。
(BindingResultの動きがわからない)

実践で使う場合、コントローラーに他の処理が混ざるので実装難易度が高くなると予想。
うまくモックできたら良いですけどね…
コントローラーのテストは苦手ですので今後の課題ですね

参考資料

https://qiita.com/ryo2132/items/ec10116238e1e1f333a1#validation-error%E3%81%8C%E3%81%82%E3%82%8B%E5%A0%B4%E5%90%88
  

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?