【Spring boot】Formのバリデーションテスト

Formのバリデーションをテストするテストコードを書いていきます。
Spring MVCではクライアントからの入力値をFormクラスにbindすることができます。
bindした値をもとに必須チェック等をFormクラス記載できます。
そのチェック内容のテストとなります。

Controllerクラスのテストコードについては下記の記事でも記載しております。
こちらにいろいろなControllerクラスのテストを記載しているので、参考にしてみてください。

テストコード

概要

ControllerクラスのテストにはMockMvcを利用します。
MockMvcとは、Spring MVCの動作を再現することができて、Controllerテストの際に使われたりするものです。

テストコードの中でFormの値を設定することが可能で、値によってエラーがあるのかないのかという検証を行います。
※今回はFormのバリデーション検証のためのコードしか記載しないので、Modelの検証等は行いません。

Formクラス

Formの内容は以下とします。
Nullを許容しない「@NotNull」
サイズをチェックする「@Size」を指定しています。

package diary.form;

import java.util.Date;

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

public class PostForm {

	private int id;

	private String categoryForm;

	@NotNull (message = "日付を入力してください。")
	private Date dateForm;

	@NotNull (message = "題名を入力してください。")
	@Size(min = 1, max = 25, message="25文字以内で入力してください。")
	private String titleForm;

	private String contentForm;

	public String getCategoryForm() {
		return categoryForm;
	}

	public void setCategoryForm(String categoryForm) {
		this.categoryForm = categoryForm;
	}

	public Date getDateForm() {
		return dateForm;
	}

	public void setDateForm(Date dateForm) {
		this.dateForm = dateForm;
	}

	public String getTitleForm() {
		return titleForm;
	}

	public void setTitleForm(String titleForm) {
		this.titleForm = titleForm;
	}

	public String getContentForm() {
		return contentForm;
	}

	public void setContentForm(String contentForm) {
		this.contentForm = contentForm;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}

Controllerクラス

Controllerの対象メソッドは以下とします。

    /**
     * 日記を新規登録
     * @param postForm
     * @param model
     * @return
     */
    @PostMapping(path="/insert", params="insert")
    public String insert(
    		@Valid @ModelAttribute PostForm form,
    		BindingResult result,
    		Model model
    		) {
    	if(result.hasErrors()) {
    		model.addAttribute("error", "パラメータエラーが発生しました。");
    		return "form";
    	}
    	int count = diaryservice.insert(form);
    	model.addAttribute("postForm", form);
    	return "redirect:/diary";
    } 

テストクラス

テストクラスを実装していきます。

package diary;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;

import java.util.Date;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.validation.BindingResult;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import diary.controller.DiaryController;
import diary.form.PostForm;

@SpringBootTest
@ActiveProfiles("unit")
public class FormTest {

	private MockMvc mockMvc;
	
	@Autowired
    private FilterChainProxy springSecurityFilterChain;
	
	@Autowired
	DiaryController target;
	
	@BeforeEach
	public void setup() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setSuffix(".html");
		mockMvc = MockMvcBuilders.standaloneSetup(target).apply(springSecurity(springSecurityFilterChain)).setViewResolvers(viewResolver).build();
	}
	
	@Test
	@WithMockUser
	public void 登録() throws Exception {
		// パラメータ設定
		PostForm form = new PostForm();
		form.setCategoryForm("1");
		form.setTitleForm("題名");
		form.setContentForm("概要");
		form.setDateForm(new Date());
		// 実行
		mockMvc.perform(post("/diary/insert")
				.param("insert", "insert")
				.flashAttr("postForm", form)
				.with(csrf()))	    
		// 検証
		// modelの検証
		// modelのいずれかにエラーが存在しないこと
		.andExpect(model().hasNoErrors())
		// postFormにエラーが存在しないこと
		.andExpect(model().attributeHasNoErrors("postForm"));
	}

	@Test
	@WithMockUser
	public void 必須エラー() throws Exception {
		// パラメータ設定
		PostForm form = new PostForm();
		form.setCategoryForm("1");
		form.setContentForm("概要");
		form.setDateForm(new Date());
		// 実行
		ResultActions results = mockMvc.perform(post("/diary/insert")
				.param("insert", "insert")
				.flashAttr("postForm", form)
				.with(csrf()))	    
		// 検証
		// modelのいずれかにエラーがあること
		.andExpect(model().hasErrors())
		// postFormにエラーが存在すること
		.andExpect(model().attributeHasErrors("postForm"));
		
		BindingResult bindResult = (BindingResult) results.andReturn().getModelAndView().getModel().get(BindingResult.MODEL_KEY_PREFIX + "postForm");
		String mes = bindResult.getFieldError().getDefaultMessage();
		// メッセージ検証
		assertEquals("題名を入力してください。", mes);
	}
	
	@Test
	@WithMockUser
	public void サイズエラー() throws Exception {
		// パラメータ設定
		PostForm form = new PostForm();
		form.setCategoryForm("1");
		form.setTitleForm("12345678901234567890123456");		
		form.setContentForm("概要");
		form.setDateForm(new Date());
		// 実行
		ResultActions results = mockMvc.perform(post("/diary/insert")
				.param("insert", "insert")
				.flashAttr("postForm", form)
				.with(csrf()))	    
		// 検証
		// modelのいずれかにエラーがあること
		.andExpect(model().hasErrors())
		// postFormにエラーが存在すること
		.andExpect(model().attributeHasErrors("postForm"));
		
		BindingResult bindResult = (BindingResult) results.andReturn().getModelAndView().getModel().get(BindingResult.MODEL_KEY_PREFIX + "postForm");
		String mes = bindResult.getFieldError().getDefaultMessage();
		assertEquals("25文字以内で入力してください。", mes);
	}
}

3つのメソッドを作成しています。
・Formがエラーにならない
・NotNullチェックのエラーになる
・Sizeチェックのエラーになる

ポイント

flashAttr
 リクエストする際のパラメータを設定しています。
 flashAttr(パラメータ名, オブジェクト)の形式で渡すことができます。
 チエックエラーになるようにメソッド毎にFormの設定値を変更しています。
 ※paramはparam(パラメータ名, 値)の形式で渡すことが可能

Modelにエラーがあるか検証
 hasErrors等を利用して、エラーがあるかを検証しています。
 hasErrorsはModelいずれかにエラーがあること
 attributeHasErrorsは指定したModelにエラーがあること
 を確認しています。(Noがつくものはエラーないことの確認となります)

エラーメッセージ検証
 エラーメッセージを「BindingResult」を使用して取得することが可能です。
 取得した値を「assertEquals」で検証しています。

テストをしてみると問題なく検証できていることが確認できます。

参考文献

ModelResultMatchers (Spring Framework 6.0.11 API)
declaration: package: org.springframework.test.web.servlet.result, class: ModelResultMatchers
タイトルとURLをコピーしました