【spring-mvc + Thymeleaf】 JUnit の設定

## はじめに

spring プロジェクトで、JUnit を使用する際に、最低限必要な設定のまとめです。
本記事は、

- [【spring-mvc + Thymeleaf】 eclipse で spring プロジェクトを作成する - Qiita](https://qiita.com/sugao-anan/items/26af0153195b1c368e61)

の続きです。


## 設定手順

### pom.xml
ライブラリの依存関係を設定します。

<details><summary>pom.xml</summary><div>

``` xml:pom.xml
  (...)
  <dependencies>
    (...)
  	<!-- #4 -->
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-test</artifactId>
  		<version>${org.springframework.version}</version>
  		<scope>test</scope>
  	</dependency>
  	<!-- #5 -->
  	<dependency>
  		<groupId>org.junit.jupiter</groupId>
  		<artifactId>junit-jupiter</artifactId>
  		<version>5.5.2</version>
  		<scope>test</scope>
  	</dependency>
  	<!-- #6 -->
  	<dependency>
  		<groupId>org.hamcrest</groupId>
  		<artifactId>hamcrest</artifactId>
  		<version>2.1</version>
  		<scope>test</scope>
  	</dependency>
  	<!-- #7 -->
  	<dependency>
  		<groupId>javax.servlet</groupId>
  		<artifactId>javax.servlet-api</artifactId>
  		<version>3.1.0</version>
  		<scope>provided</scope>
  	</dependency>
  	<!-- #8 -->
    <dependency>
    	<groupId>org.mockito</groupId>
    	<artifactId>mockito-junit-jupiter</artifactId>
    	<version>3.5.11</version>
    	<scope>test</scope>
    </dependency>
  	<!-- #9 -->
    <dependency>
    	<groupId>org.jmockit</groupId>
    	<artifactId>jmockit</artifactId>
    	<version>1.49</version>
    	<scope>test</scope>
    </dependency>
  </dependencies>
  (...)
```
</div></details>

### spring-test

#### 依存関係

No. | groupId | artifactId | version| scope
----:|------------ | ------------- | ------------- | -------------
4 | org.springframework | [spring-test](https://mvnrepository.com/artifact/org.springframework/spring-test) | ${org.springframework.version} | test
5 | org.junit.jupiter | [junit-jupiter](https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter) | 5.5.2 | test
6 | org.hamcrest | [hamcrest](https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api) | 2.1 | test
7 | javax.servlet | [javax.servlet-api](https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api) | 3.1.0 | provided

**メモ**
- `hamcrest` (#6) が無いと、`view()` はビルドエラーになります。
- `javax.servlet-api` (#7) は、無くてもビルドできるが、JUnit実行時に以下のエラーが発生します。

```
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.test.context.web.WebDelegatingSmartContextLoader]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: javax/servlet/ServletContext
(...)
```

#### テストコード
controller のテストコードを追加します。

<details><summary>HomeControllerTest.java</summary><div>

``` java:src/test/java/com/example/demo/app/welcome/HomeControllerTest.java
package com.example.demo.app.welcome;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
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.web.context.WebApplicationContext;

@SpringJUnitWebConfig(locations = "file:src/main/webapp/WEB-INF/spring/dispatcher-config.xml")
public class HomeControllerTest {

	private MockMvc mockMvc;

	@Autowired
	private WebApplicationContext wac; //コンテキストを用意

	@BeforeEach
	public void setUp() {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}

	@Test
	public void testHomeController() throws Exception {
		final ResultActions result = mockMvc.perform(get("/"));
		result.andExpect(status().isOk());
		result.andExpect(view().name("welcome/home"));
	}
}
```
</div></details>

### spring-test + mockito

#### 依存関係

No. | groupId | artifactId | version| scope
----:|------------ | ------------- | ------------- | -------------
8 | org.mockito | [mockito-junit-jupiter](https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter) | 3.5.11 | test

**メモ**
- `junit5` で `mockito` を使用する場合は、`mockito-junit-jupiter` (#8) があります。
- `mockito-junit-jupiter` (#8) を使用する場合は、`junit-jupiter` (#5) は不要です。

#### テストコード
controller のテストコードを追加します。

<details><summary>HomeControllerTestWithMockito.java</summary><div>

``` java:src/test/java/com/example/demo/app/welcome/HomeControllerTestWithMockito.java
package com.example.demo.app.welcome;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;	// mockito
import org.mockito.junit.jupiter.MockitoExtension;	// mockito
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

@ExtendWith(MockitoExtension.class)	// mockito
@SpringJUnitWebConfig(locations = "file:src/main/webapp/WEB-INF/spring/dispatcher-config.xml")
public class HomeControllerTestWithMockito {

	private MockMvc mockMvc;

	@InjectMocks	// mockito
	HomeController homeController;

	@BeforeEach
	public void setUp() {
		mockMvc = MockMvcBuilders.standaloneSetup(homeController).build();
	}

	@Test
	public void testHomeController() throws Exception {
		final ResultActions result = mockMvc.perform(get("/"));
		result.andExpect(status().isOk());
		result.andExpect(view().name("welcome/home"));
	}
}
```
</div></details>

### spring-test + jmockit

#### 依存関係

No. | groupId | artifactId | version| scope
----:|------------ | ------------- | ------------- | -------------
9 | org.jmockit | [jmockit](https://mvnrepository.com/artifact/org.jmockit/jmockit) | 1.49 | test

#### テストコード
controller のテストコードを追加します。

<details><summary>HomeControllerTestWithJMockit.java</summary><div>

``` java:src/test/java/com/example/demo/app/welcome/HomeControllerTestWithJMockit.java
package com.example.demo.app.welcome;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import mockit.Tested;	// jmockit

@SpringJUnitWebConfig(locations = "file:src/main/webapp/WEB-INF/spring/dispatcher-config.xml")
public class HomeControllerTestWithJMockit {

	private MockMvc mockMvc;

	@Tested(availableDuringSetup=true)	// jmockit
	HomeController homeController;

	@BeforeEach
	public void setUp() {
		mockMvc = MockMvcBuilders.standaloneSetup(homeController).build();
	}

	@Test
	public void testHomeController() throws Exception {
		final ResultActions result = mockMvc.perform(get("/"));
		result.andExpect(status().isOk());
		result.andExpect(view().name("welcome/home"));
	}
}
```
</div></details>

## ソース一式
ソース一式は以下の場所にあります。

- [https://github.com/sugao-anan/hello-spring/releases/tag/v0.1.1.1](https://github.com/sugao-anan/hello-spring/releases/tag/v0.1.1.1)

## 参考にしたサイト

- [Spring5入門\[簡単なWebアプリのユニットテストをJUnit5とJMockitで作成\] - Small Java world](https://javaworld.helpfulness.jp/post-333/)
- [Spring テスト - 公式ドキュメントの日本語訳](https://spring.pleiades.io/spring/docs/current/spring-framework-reference/testing.html)
- [JUnit 5 ユーザガイド](https://udzuki.jp/public/junit5-user-guide-ja/)


以上