Spring Boot + SpockでRestControllerのテストをサクッと書く


## テスト内容
RestControllerが返却するJSON形式のレスポンスボディを検証します。

## 環境
Spring Boot：2.1.1
Spock：1.2
Groovy：2.5
OS：Windows10

## 準備

Spring Boot + Spockのテストで必要なライブラリを追加します。spock-coreへの依存を書いておくことで、Groovy周りのライブラリも一緒に落ちてきます。また、spock-springがないとSpock（Groovy）の中で@Autowired等が使えないので、必要であればこれも追加しておきます。

```pom.xml
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.spockframework</groupId>
	<artifactId>spock-core</artifactId>
	<version>1.2-groovy-2.5</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.spockframework</groupId>
	<artifactId>spock-spring</artifactId>
	<version>1.2-groovy-2.5</version>
	<scope>test</scope>
</dependency>
```

## テストしたいRestControllerのURLとレスポンスボディ
テストしたいRestControllerのURLとレスポンスボディは以下のものとします。

```
URL：
  http://localhost:8080/employees

Response：
  Status Code 200 OK
  [
    {
      "id": "00000001",
      "name": "社員A",
      "department": {
        "id": "001",
        "name": "部署A"
      }
    },
    {
      "id": "00000002",
      "name": "社員B",
      "department": {
        "id": "001",
        "name": "部署A"
      }
    }
  ]
```

## テストしたいControllerクラス
EmployeeService#getEmployeesから先は省略しますが、テストしたいControllerとしては以下のような形です。

```EmployeeController.java
@RestController
public class EmployeeController {

	@Autowired
	private EmployeeService employeeService;

	@GetMapping(value = "/employees")
	public ResponseEntity<List<Employee>> getEmployees() {
		List<Employee> employeeList = employeeService.getEmployees();
		if (CollectionUtils.isEmpty(employeeList)) {
			return new ResponseEntity<>(null, HttpStatus.NO_CONTENT);	
		}
		return new ResponseEntity<List<Employee>>(employeeList, HttpStatus.OK);
	}
}
```

## テストクラス

GroovyのJsonSlurperとJsonBuilderを利用して検証します。

* Spring BootのテストでMockMvcを使う場合、テストクラスに@AutoConfigureMockMvcを付与します。そうすることで、@AutowiredでMockMvcのインジェクションが可能になります。
* 2つめのテストケースとして、レスポンスボディに何も設定されていないケースも書いてみます。そのため、そのケースのみ@SpyBeanを利用してEmployeeService#getEmployeesがnullを返すようスパイ化します（@MockBeanを利用すると、ケース1のEmployeeServiceもMock化されてしまうため）。
* @SpringBootTestを付与した場合、デフォルトではサーバーは起動しません。そのため、そのあたりを柔軟に対応したい場合はwebEnvironment属性を使用することになります（今回の例では使用しません）。

```
// ApplicationContextはロードするが、Web環境等は提供しない
@SpringBootTest(webEnvironment = WebEnvironment.NONE)

// サーバーをランダムなポートで起動させる
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)


// デフォルトはコレ、特に書かなくてもよい。サーバーを起動させず模擬Web環境が提供される
// @AutoConfigureMockMvcまたは@AutoConfigureWebTestClientと組み合わせて使用できる
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)


// 実際のWeb環境が提供される。組み込みサーバーは起動し、設定されたポート（application.propertiesから）またはデフォルトのポート8080でlistenする
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
```

```EmployeeControllerTest.groovy
@SpringBootTest
@AutoConfigureMockMvc
class EmployeeControllerTest extends Specification {

	@SpyBean
	private EmployeeService employeeService

	@Autowired
	MockMvc mockMvc

	@Unroll
	def "EmployeeController - 正常系 - 2件の社員が返る"() {

		given:
		def department = new Department("001", "部署A");
		def employeeList = new ArrayList<Employee>() { {
						this.add(new Employee("00000001", "社員A", department));
						this.add(new Employee("00000002", "社員B", department));
					}
				}
		def jsonBuilder = new JsonBuilder(employeeList)
		def jsonSlurper = new JsonSlurper()

		when:
		def actual = mockMvc.perform(MockMvcRequestBuilders.get("/employees")).andReturn().getResponse()

		then:
		actual.getStatus() == HttpStatus.OK.value
		jsonSlurper.parseText(actual.getContentAsString()) == jsonSlurper.parseText(jsonBuilder.toPrettyString())
	}

	@Unroll
	def "EmployeeController - 正常系 - 何も返らない"() {

		given:
		when(employeeService.getEmployees()).thenReturn(null)

		when:
		def actual = mockMvc.perform(MockMvcRequestBuilders.get("/employees")).andReturn().getResponse()

		then:
		actual.getStatus() == HttpStatus.NO_CONTENT.value
		actual.getContentAsString() == ""
	}
}
```

本記事では、JSONの検証にGroovyのJsonSlurperとJsonBuilderを利用しました。他の方法として、[Spring Bootの@JsonTest](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-testing-autoconfigured-json-tests)等を利用してもJSONの検証は可能です。

本記事で使用したソース：https://github.com/kenichi-nagaoka/spock-sample/tree/feature/1

以上です。

## 参考
* https://spock-framework-reference-documentation-ja.readthedocs.io/ja/latest/index.html