Spring BootでTestRestTemplateを試す
Spring BootでTestRestTemplateを試してみたメモです。
TestRestTemplate
httpクライアントとしてRestTemplateがあります。
TestRestTemplateはRestTemplateのテスト用のクラスで、ベーシック認証のサポートなどテスト用に便利になっているようです。
Spring Boot1.4からRestTemplateを継承しなくなったようです。
http通信はデフォルトではJava標準のHttpURLConnectionが使われるようです。
今回テストのRESTクライアントとして使ってみます。
テスト対象クラス
テスト対象のcontrollerとして下記のクラスを用意しました。
GET、POST、PUT、DELETEを受け付けます。
PeopleController.java
package com.example.contoroller; import lombok.AccessLevel; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.FieldDefaults; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class PeopleController { @ResponseBody @RequestMapping(value = "/api/people", method = RequestMethod.GET) public People getPeople() { People people = new People(); people.setCountry("Japan"); people.setYear(2001); people.setPopulation(1_000_000); return people; } @ResponseBody @RequestMapping(value = "/api/people", method = RequestMethod.POST) public int postPeople(@RequestBody People people) { // 登録処理 // 登録件数を返す return 1; } @ResponseBody @RequestMapping(value = "/api/people", method = RequestMethod.PUT) public int putPeople(@RequestBody People people) { // 更新処理 // 更新件数を返す return 2; } @ResponseBody @RequestMapping(value = "/api/people/{country}", method = RequestMethod.DELETE) public int deletePeople(@PathVariable String country) throws Exception { // 削除処理 // 削除件数を返す return 3; } @Data @NoArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) public static class People { String country; int year; int population; } }
GETメソッドはPeopleオブジェクトを返します。
@ResponseBody @RequestMapping(value = "/api/people", method = RequestMethod.GET) public People getPeople() { People people = new People(); people.setCountry("Japan"); people.setYear(2001); people.setPopulation(1_000_000); return people; }
POSTメソッドは、RequestBodyでPeopleオブジェクトを受け取り、登録件数を返します。
@ResponseBody @RequestMapping(value = "/api/people", method = RequestMethod.POST) public int postPeople(@RequestBody People people) { // 登録処理 // 登録件数を返す return 1; }
PUTメソッドは、RequestBodyでPeopleオブジェクトを受け取り、更新件数を返します。
@ResponseBody @RequestMapping(value = "/api/people", method = RequestMethod.PUT) public int putPeople(@RequestBody People people) { // 更新処理 // 更新件数を返す return 2; }
DELETEメソッドは、PathVariableで削除対象の国名を受け取り、削除件数を返します。
@ResponseBody @RequestMapping(value = "/api/people/{country}", method = RequestMethod.DELETE) public int deletePeople(@PathVariable String country) throws Exception { // 削除処理 // 削除件数を返す return 3; }
テストコード
TestRestTemplateを使ってテストしてみました。
テストクラスとして
(1),(2) @RunWith(SpringRunner.class)と@SpringBootTestを指定します。
(1) Spring Boot1.4から@RunWith(SpringJUnit4ClassRunner.class)が@RunWith(SpringRunner.class)に変わりました。
(2) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)で
空いているランダムなポートでテストを行います。
@RunWith(SpringRunner.class) // (1) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // (2) public class PeopleControllerTest {
(1) @AutoWiredでTestRestTemplateをインジェクトして、
(2) @LocalServerPortで割り当てられたポートをインジェクトします。
@Autowired // (1) private TestRestTemplate testRestTemplate; @LocalServerPort // (2) private int port;
GETのテスト
GETの戻り値がオブジェクトの場合、
TestRestTemplate.getForEntity(String url, Class<T> responseType, Object... urlVariables)を使用します。
urlでRESTのAPIを指定して、responseTypeでAPIの戻り値のクラスを指定します。
パス変数を指定する場合はurlVariablesを可変長で設定します。
@Test public void getPeopleTest() { String url = "http://localhost:" + port + "/api/people"; People people = testRestTemplate.getForObject(url, People.class); People expected = new People(); expected.setCountry("Japan"); expected.setYear(2001); expected.setPopulation(1_000_000); assertThat(people).isEqualTo(expected); }
POSTのテスト
POSTで任意の戻り値を受けるには、
TestRestTemplate.postForObject(String url, Object request, Class<T> responseType, Object... urlVariables)を使用します。
urlでREST APIのURLを指定して、requestでRequestBodyのオブジェクトを指定します。
responseTypeでAPIの戻り値のクラスを指定します。
パス変数を指定する場合はurlVariablesを可変長で設定します。
@Test public void postPeopleTest() { String url = "http://localhost:" + port + "/api/people"; People people = new People(); people.setCountry("America"); people.setYear(2002); people.setPopulation(2_000_000); int result = testRestTemplate.postForObject(url, people, Integer.class); assertThat(result).isEqualTo(1); }
PUTのテスト
(1) PUTのテストではTestRestTemplate.put(String url, Object request, Object... urlVariables)が使用できます。
urlでREST APIのURLを指定して、requestでRequestBodyのオブジェクトを指定します。
パス変数を指定する場合はurlVariablesを可変長で設定します。
しかし、TestRestTemplate.put()は戻り値がvoidのため、REST API側で戻り値を返す場合、取得できません。
戻り値を取得したい場合TestRestTemplate.exchange()を使用すると、
ResponseEntityが返るのでREST APIの戻り値を取得することができます。
(2) RequestEntityを生成します。RequestEntity<People>でbodyの型を記述し、
putでRESTのPUT APIを指定し、bodyでRequestBodyのオブジェクトを指定します。
(3) exchange()でRequestEntityと戻り値のクラスを指定します。
戻り値としてResponseEntityが返ります。ResponseEntity.getBody()でAPIの戻り値を取得できます。
@Test public void putPeopleTest() throws URISyntaxException { String url = "http://localhost:" + port + "/api/people"; People people = new People(); people.setCountry("America"); people.setYear(2002); people.setPopulation(2_000_000); // put testRestTemplate.put(url, people); // (1) // exchange URI uri = new URI(url); RequestEntity<People> requestEntity = RequestEntity // (2) .put(uri) .body(people); ResponseEntity<Integer> result = testRestTemplate.exchange(requestEntity, Integer.class); // (3) assertThat(result.getBody()).isEqualTo(2); }
DELETEのテスト
(1) DELETEのテストではTestRestTemplate.delete(String url, Object... urlVariables)が使用できます。
urlでREST APIのURLを指定して、urlVariablesでパス変数を可変長で設定します。
しかし、TestRestTemplate.delete()は戻り値がvoidのため、REST API側で戻り値を返す場合、取得できません。
戻り値を取得したい場合TestRestTemplate.exchange()を使用すると、
ResponseEntityが返るのでREST APIの戻り値を取得することができます。
(2) DELETEの場合、pathにcountryを指定する必要があるので、
exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... urlVariables)
を使用します。
引数として、urlでRESTのDELETE APIを指定し、methodでHttpMethod.DELETEを指定します。
requestEntityでHttpEntityのオブジェクトを指定します。http headerを設定する場合はここで指定しますが、
今回は不要なのでHttpEntity.EMPTYを指定しています。
responseTypeで戻り値の型を指定します。
urlVariablesでパス変数を指定します。今回はcountryを指定します。
戻り値としてResponseEntityが返ります。ResponseEntity.getBody()でAPIの戻り値を取得できます。
@Test public void deletePeopleTest() { String url = "http://localhost:" + port + "/api/people/{country}"; String country = "america"; // delete testRestTemplate.delete(url, country); // exchange ResponseEntity<Integer> result = testRestTemplate.exchange(url, // (2) HttpMethod.DELETE, HttpEntity.EMPTY, Integer.class, country); assertThat(result.getBody()).isEqualTo(3); }
今回のテストコードの全体です。
PeopleControllerTest.java
package com.example.contoroller; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; import com.example.contoroller.PeopleController.People; import java.net.URI; import java.net.URISyntaxException; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class PeopleControllerTest { @Autowired private TestRestTemplate testRestTemplate; @LocalServerPort private int port; @Test public void getPeopleTest() { String url = "http://localhost:" + port + "/api/people"; People people = testRestTemplate.getForObject(url, People.class); People expected = new People(); expected.setCountry("Japan"); expected.setYear(2001); expected.setPopulation(1_000_000); assertThat(people).isEqualTo(expected); } @Test public void postPeopleTest() { String url = "http://localhost:" + port + "/api/people"; People people = new People(); people.setCountry("America"); people.setYear(2002); people.setPopulation(2_000_000); int result = testRestTemplate.postForObject(url, people, Integer.class); assertThat(result).isEqualTo(1); } @Test public void putPeopleTest() throws URISyntaxException { String url = "http://localhost:" + port + "/api/people"; People people = new People(); people.setCountry("America"); people.setYear(2002); people.setPopulation(2_000_000); // put testRestTemplate.put(url, people); // exchange URI uri = new URI(url); RequestEntity<People> requestEntity = RequestEntity .put(uri) .body(people); ResponseEntity<Integer> result = testRestTemplate.exchange(requestEntity, Integer.class); assertThat(result.getBody()).isEqualTo(2); } @Test public void deletePeopleTest() { String url = "http://localhost:" + port + "/api/people/{country}"; String country = "america"; // delete testRestTemplate.delete(url, country); // exchange ResponseEntity<Integer> result = testRestTemplate.exchange(url, HttpMethod.DELETE, HttpEntity.EMPTY, Integer.class, country); assertThat(result.getBody()).isEqualTo(3); } }
ソースは一応あげときました。
終わり。
【参考】
spring徹底入門を書いている清水さんの記事がかなり詳しいです。
SpringのRestTemplateを使うコンポーネントのJUnitテストはこう書く!! - Qiita