今回は、Spring Framework 4.3の変更点紹介シリーズの最終回で、テスト関連の変更点を紹介します。
(リリース前の投稿を更新しました!! 差分は、「★6/11追加」でマークしてあります)
シリーズ
- 第1回:Spring 4.3 DIコンテナ関連の主な変更点
- 第2回:Spring 4.3 データアクセス関連の主な変更点
- 第3回:Spring 4.3 キャッシュ関連の主な変更点
- 第4回:Spring 4.3 JMS関連の主な変更点
- 第5回:Spring 4.3 Web関連の主な変更点
- 第6回:Spring 4.3 WebSocket関連の主な変更点
動作検証環境
- Spring Framework 4.3.0.RELEASE
- Spring Boot 1.4.0.BUILD-SNAPSHOT (2016/6/11時点)
Testing Improvements
今回は、テスト関連の主な変更点をみていきます。
No | テスト関連の主な変更点 |
---|---|
1 | Spring TestContext FrameworkのJUnitの必須バージョンが4.12以上になります。 |
2 |
SpringJUnit4ClassRunner クラスの別名としてSpringRunner クラスが追加されます。 |
3 | Spring Testが提供しているテスト用のアノテーションをインタフェースに定義することで、複数のテストクラスでアノテーションの定義を共有できるようになります。 「★6/11追加」 |
4 | 自動検出されるテスト用のBean定義ファイルを利用する場合は、@ContextConfiguration は省略できるようになります。 |
5 |
@Transactional は、public以外のテストメソッドにも適用できます。(TestNGやJUnit 5向けの対応) |
6 |
@BeforeTransaction 、@AfterTransaction は、public以外のテストメソッドにも指定できるようになり、さらにJava 8からサポートされたインタフェースのデフォルトメソッドに指定することができます。 |
7 | Spring TestContext Framework内でキャッシュするアプリケーションコンテキストの数に上限を設けられるようになります。デフォルトの動作では、キャッシュの最大数は32個で最大数をこえると最も使われていないものから破棄されていきます。なお、キャッシュの最大数は、プロパティ(spring.test.context.cache.maxSize )で変更できます。 |
8 | Bean定義をカスタマイズするためのインタフェース(ContextCustomizer )が追加され、アプリケーションコンテキスト内にBean定義を読み込んだ直後にコールバックされます。 |
9 |
@Sql と@SqlGroup をメタアノテーションとして利用できるようになります。 |
10 |
ReflectionTestUtils にて、インタフェースベースのProxyオブジェクトに対するフィールドアクセスが可能になります。 「★6/11追加」 |
11 | サーバーサイド向けSpring MVC Test Framework(MockMvc )にて、複数の値をもつレスポンスヘッダの検証がサポートされます。 |
12 | サーバーサイド向けSpring MVC Test Framework(MockMvc )にて、リクエストボディに指定したフォームデータ(application/x-www-form-urlencoded 形式)がリクエストパラメータとして扱われるようになります。 |
13 | サーバーサイド向けSpring MVC Test Framework(MockMvc )にて、呼び出されたHandlerメソッドの検証を行うことができるようになります。 「★6/11追加」 |
14 | クライアントサイド向けモックRESTサーバ機能(MockRestServiceServer )にて、モックレスポンスを返却する回数などを指定できるようになります。 |
15 | クライアントサイド向けモックRESTサーバ機能(MockRestServiceServer )にて、リクエストボディに指定したフォームデータ(application/x-www-form-urlencoded 形式)の検証がサポートされます。 |
JUnitの必須バージョンが4.12以上になる
Spring 4.3から、Junitを使う場合は4.12以上のバージョンが必要になります。Spring Bootの最新バージョン(1.3.5.RELEASE)はすでに4.12に対応しているため、Spring Bootユーザーは意識する必要はないでしょう。
Spring Bootユーザーではない方や、独自にJUnitの依存定義を行っている場合は、JUnitのバージョンを変更してください。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
SpringRunner
クラスが追加される
Spring 4.3から、SpringJUnit4ClassRunner
の別名クラスとしてSpringRunner
クラスが追加されます。名前が短くてナイスです 利用できる機能はSpringJUnit4ClassRunner
と全く一緒で、SpringJUnit4ClassRunner
も引き続き利用可能です。
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest {
// ...
}
@RunWith(SpringRunner.class) // 名前が短くてシンプル!!
public class SpringTest {
// ...
}
テスト用のアノテーションをインタフェースに定義できる
Spring 4.3から、Spring Testが提供しているテスト用のアノテーションをインタフェースに定義することで、複数のテストクラスでアノテーションの定義を共有できるようになります。
インタフェースに定義可能なアノテーションについては、Spring JIRAのSPR-14184を参照してください!!
package com.example;
import org.springframework.test.context.ActiveProfiles;
@ActiveProfiles("unittest") // インタフェースにアノテーションを付与する
public interface ActivateUnitTestProfiles {
}
package com.example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.Clock;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
public class ProfileTests implements ActivateUnitTestProfiles { // インタフェースを実装する
@Autowired
Clock clock;
@Test
public void testClock() {
assertThat(ZonedDateTime.now(clock))
.isEqualTo(ZonedDateTime.of(2016, 6, 11, 0, 0, 0, 1, ZoneId.systemDefault()));
}
}
@Configuration
public class ClockConfig {
@Bean
@Profile("prod")
public Clock prodClock() {
return Clock.systemDefaultZone();
}
@Bean
@Profile("dev")
public Clock devClock() {
return Clock.systemDefaultZone();
}
@Bean
@Profile("unittest") // このBean定義が使われる
public Clock unittestClock() {
return Clock.fixed(ZonedDateTime.of(2016, 6, 11, 0, 0, 0, 1, ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
}
}
インタフェースなので、目的毎(例えば、Web用、バッチ用、データアクセス用など)に作成したインタフェースをミックスインして利用することもできます。また、Java 8から追加されたデフォルトメソッドの仕組みを利用してユーティリティ的なメソッドを用意することができるため、テストクラスでは、テストケースの作成に集中しやすくなります。
なお、それぞれのアノテーションのサンプルは、Spring Testの「テストケースクラスのソースコード」が参考になります。
@ContextConfiguration
が省略できる
Spring 4.3から、@ContextConfiguration
が省略できるようになります。@ContextConfiguration
を省略する場合は、Springが自動検出するBean定義(クラス or ファイル)を用意する必要があります。(ちなみに・・・用意しなくてもエラーにはなりませんが、DIコンテナにテスト対象のBeanなどが登録できないので意味ないですよね )
この仕組みは、本格的なテストを行う時にはあまり使う機会はない気もしますが・・・ちょっとした動作検証をJUnit上で行う時に非常に便利で、私もよく使います
Note: Spring 4.2までは・・・
同等の仕組みは提供されていますが、必ず@ContextConfiguration
を付与する必要がありました。
Springは、以下のBean定義(クラス or ファイル)を自動検出しますが、併用はできません。
-
@Configuration
が付与されたstaticインナークラス - ネーミングルールで解決されるXML形式のBean定義ファイル
@Configuration
が付与されたstaticインナークラスの利用
テストケースクラス内に、Java Configクラスをstaticなインナークラスとして定義します。
@RunWith(SpringRunner.class)
// @ContextConfiguration ← Spring 4.3から省略可能
public class SpringTest {
@Configuration
static class LocalContext {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
}
// ...
}
ネーミングルールで解決されるXML形式のBean定義ファイルの利用
テストケースクラスと同じディレクトリ階層に、「テストケースのクラス名-context.xml」という名前のXML形式のBean定義ファイルを配置します。
package com.example;
// ...
@RunWith(SpringRunner.class)
// @ContextConfiguration ← Spring 4.3から省略可能
public class SpringTest {
// ...
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
">
<jdbc:embedded-database type="H2" id="dataSource"/>
</beans>
@Transactional
をpublic以外のテストメソッドにも適用できる
Spring 4.3から、@Transactional
をpublic以外のテストメソッドに適用できるようになります。これは、「TestNG」や「JUnit 5」向けの対応のようです。本投稿では特に説明はしません。(あしからず・・・)
ちなみに・・・SpringのJUnit 5対応については「SPR-13575」をご覧ください。プロトタイププロジェクト (spring-test-junit5)がGitHub上に公開されており、Spring 5でSpring本体に組み込まれるみたいです。
@BeforeTransaction
、@AfterTransaction
をインタフェースのデフォルトメソッドに適用できる
Spring 4.3から、Java 8からサポートされたインタフェースのデフォルトメソッドに@BeforeTransaction
、@AfterTransaction
を適用できるようになります。また、本投稿では説明はしませんが、@Transactional
と同様にpublic以外のテストメソッドにも適用できるようになります。
本投稿では、Java 8からサポートされたインタフェースのデフォルトメソッドで@BeforeTransaction
、@AfterTransaction
を利用する方法をみていきます。
package com.example;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
public interface TransactionSupports {
@BeforeTransaction
default void beforeTx() {
System.out.println("before transaction !!");
// Please implements your code
}
@AfterTransaction
default void afterTx() {
System.out.println("after transaction !!");
// Please implements your code
}
}
デフォルトメソッドを実装したインタフェースをテストケースクラスで実装することで、@BeforeTransaction
メソッドと@AfterTransaction
メソッドをテストケースクラスにミックスインすることができます。
@RunWith(SpringRunner.class)
@Transactional
public class SpringTest implements TransactionSupports { // デフォルトメソッドを実装したインタフェースを実装するだけ!!
// ...
}
この仕組みを利用すると、@BeforeTransaction
メソッドと@AfterTransaction
メソッドの実装を簡単に複数のクラスで共有できます。抽象クラスではなくインタフェースなので、複数のインタフェースに実装されているメソッドをミックスインもできます!!
アプリケーションコンテキストのキャッシュ数に上限を設けることができる
Spring TestContext Framework内でキャッシュするアプリケーションコンテキストの数に上限を設けられるようになります。デフォルトの動作では、キャッシュの最大数は32個で最大数をこえると最も使われていないものから破棄(LRU : Least Recently Used)されていきます。なお、キャッシュの最大数は、プロパティ(spring.test.context.cache.maxSize
)で変更できます。
プロパティ値はプロパティファイルとシステムプロパティで指定できますが、指定が重複している場合はプロパティファイルの値が優先されます。
Springプロパティファイルを利用して最大値を変更
クラスパス直下にspring.properties
を作成して最大値を指定します。
spring.test.context.cache.maxSize=64
システムプロパティを利用して最大値を変更
-Dspring.test.context.cache.maxSize=64
Bean定義をカスタマイズするためのインタフェース(ContextCustomizer
)が追加される
Spring 4.3から、Bean定義をカスタマイズするためのインタフェース(ContextCustomizer
)が追加され、アプリケーションコンテキスト内にBean定義を読み込んだ直後にコールバックされます。このインタフェースの実装クラスを作成することで、テスト用のアプリケーションコンテキストをカスタマイズできます。なお、Spring BootはContextCustomizer
の仕組みを利用して、テスト用のアプリケーションコンテキストをSpring Bootアプリケーションのテスト向けにカスタマイズしているようです。
では、自作のContextCustomizer
をSpring TestContext Frameworkに適用する方法をみていきましょう。まず、ContextCustomizer
インタフェースの実装クラスを作成します。
package com.example;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
public class MyContextCustomizer implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
System.out.println("customizeContext !!");
// Please implements your code.
}
}
次に、作成したMyContextCustomizer
クラスのインスタンスを生成するためのファクトリクラスを作成します。
package com.example;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import java.util.List;
public class MyContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes) {
return new MyContextCustomizer();
}
}
最後に、クラスパス上の/META-INF/spring.factories
に作成したファクトリクラスを登録します。(ファイルがなければ作成してください)
# Spring Test ContextCustomizerFactories
org.springframework.test.context.ContextCustomizerFactory=com.example.MyContextCustomizerFactory
@Sql
と@SqlGroup
をメタアノテーションとして利用できる
Spring 4.3から、@Sql
と@SqlGroup
をメタアノテーションとして利用できるようになります。
ここでは、各優先度が高いメッセージのみを扱うリスナーであることを表現するカスタムアノテーションを使ってみます。
@Sql("classpath:sql/clear.sql")
@Sql("classpath:sql/load.sql")
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface InitializingSqls {
}
@SqlGroup({
@Sql("classpath:sql/clear.sql")
, @Sql("classpath:sql/load.sql")
})
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface InitializingSqls {
}
テストケースクラス側では、クラスレベルまたはメソッドレベルにカスタムアノテーションを指定するだけです。
@RunWith(SpringRunner.class)
@Transactional
@InitializingSqls // カスタムアノテーションを指定
public class SpringTests implements TransactionSupports {
// ...
}
インタフェースベースのProxyオブジェクトに対するフィールドアクセスが可能になる
Spring 4.3にて、テスト用のユーティリティクラス(ReflectionTestUtils
)を介してインタフェースベースのProxyオブジェクトに対するフィールドアクセスが可能になります。個人的には、ReflectionTestUtils
を使わなくても済むようにクラス設計、テスト設計すべきだと思いますが・・・ (ま、Spring Testにはこんなユーティリティもあるんだ〜くらいに思っておけばいいかと思います・・・)
package com.example;
public interface TodoService {
void create(Todo todo);
}
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
@Service
@Transactional // インタフェースベースのproxyオブジェクトになる
public class TodoServiceImpl implements TodoService {
@Autowired
private RestOperations restOperations;
public void create(Todo todo) {
// ...
}
}
@Autowired
TodoService todoService; // Proxyオブジェクトがインジェクションされる
@Test
public void createTodo() {
RestOperations restOperations = Mockito.mock(RestOperations.class);
// ...
ReflectionTestUtils.setField(todoService, "restOperations", restOperations); // フィールドアクセスしてモックを設定
todoService.create(new Todo());
// ...
}
MockMvc
で複数の値をもつレスポンスヘッダの検証ができる
Spring 4.3から、サーバーサイド向けSpring MVC Test Framework(MockMvc
)にて、複数の値をもつレスポンスヘッダの検証用メソッドが追加されます。
...
Cache-Control: max-age=86400
Cache-Control: must-revalidate
...
@RunWith(SpringRunner.class)
@WebAppConfiguration
public class SpringTest {
@Autowired
WebApplicationContext wac;
MockMvc mockMvc;
@Before
public void setUpMockMvc() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void getMultiResponseHeader() throws Exception {
mockMvc.perform(get("/"))
.andExpect(header()
.stringValues("Cache-Control", "max-age=86400", "must-revalidate")); // 複数のヘッダ値の検証が可能
}
}
MockMvc
でフォーム形式のボディをリクエストパラメータとして扱える
Spring 4.3から、サーバーサイド向けSpring MVC Test Framework(MockMvc
)にて、リクエストボディに指定したフォームデータ(application/x-www-form-urlencoded
形式)がリクエストパラメータとして扱われるようになります。 ここでは、param1
とparam2
というふたつのリクエストパラメータを扱う例をみてみましょう。
@Controller
public class TestController {
@RequestMapping(path = "/post", method = RequestMethod.POST)
public String post(@RequestParam String param1, @RequestParam String param2, Model model) {
model.addAttribute("param1", param1);
model.addAttribute("param2", param2);
return "confirm";
}
}
Spring 4.2までは、param
メソッドを使ってリクエストパラメータを指定する必要がありました。もちろんSpring 4.3以降でもこの方法は利用できます。
@Test
public void form() throws Exception {
mockMvc.perform(post("/post")
.param("param1", "aaaa").param("param2", "bbbb")) // paramメソッドを使ってリクエストパラメータの指定
.andExpect(status().isOk())
.andExpect(view().name("confirm"))
.andExpect(model().attribute("param1", "aaaa"))
.andExpect(model().attribute("param2", "bbbb"));
}
Spring 4.3からは、content
メソッドにapplication/x-www-form-urlencoded
形式のボディを指定することで同じことを実現することができます。
@Test
public void form() throws Exception {
mockMvc.perform(post("/post")
.contentType(MediaType.APPLICATION_FORM_URLENCODED) // Content-Typeに「application/x-www-form-urlencoded」を指定
.content("param1=aaaa¶m2=bbbb")) // リクエストボディに「application/x-www-form-urlencoded」形式の値を指定
.andExpect(status().isOk())
.andExpect(view().name("confirm"))
.andExpect(model().attribute("param1", "aaaa"))
.andExpect(model().attribute("param2", "bbbb"));
}
Content-TypeにMediaType.APPLICATION_FORM_URLENCODED
を指定するのがポイントです。指定しないと、リクエストパラメータとして扱われません。
呼び出されたHandlerメソッドの検証ができる
Spring 4.3から、サーバーサイド向けSpring MVC Test Framework(MockMvc
)にて、呼び出されたHandlerメソッドの検証を行うことができるようになります。
@Test
public void getAll() throws Exception {
mockMvc.perform(get("/todos"))
.andExpect(status().isOk())
// 呼び出されたメソッドを検証(MvcUriComponentsBuilderのonメソッドを利用)
.andExpect(handler().methodCall(on(TodoRestController.class).getAll()))
// 呼び出されたHandlerメソッドのクラスを検証
.andExpect(handler().handlerType(TodoRestController.class))
// 呼び出されたメソッドを検証
.andExpect(handler().method(TodoRestController.class.getMethod("getAll")))
// 呼び出されたメソッドの名前を検証
.andExpect(handler().methodName("getAll"));
}
いくつか方法が用意されていますが、MvcUriComponentsBuilder
を使う方法がタイプセーフな実装なのでオススメです。
MockRestServiceServer
でモックレスポンスを返却する回数を指定できる
Spring 4.3から、クライアントサイド向けモックRESTサーバ機能(MockRestServiceServer
)にて、モックレスポンスを返却する回数や順序性を無視するためのフラグなどを指定できるようになります。 MockRestServiceServer
はあまり知られていない!?気がしますが、外部のREST APIなどにアクセスするようなアプリケーションのテストを行う場合に非常に便利な機能です。
MockRestServiceServer
の使い方
すご〜く簡単にMockRestServiceServer
の使い方を紹介しておきます。たとえば、以下のようなControllerのテストを考えてみましょう。このControllerクラスでは、外部のREST APIを呼び出した結果を画面に表示しています。外部のREST APIを呼び出す際には、Springが提供しているRestOperations
インターフェースを介してRestTemplate
クラスを利用します。
@RequestMapping("/todos")
@Controller
static class TodoController {
@Autowired
RestOperations restOperations;
@RequestMapping(path = "{todoId}", method = RequestMethod.GET)
public String detail(@PathVariable String todoId, Model model) {
Todo todo = restOperations.getForObject("https://api.domain/todos/{todoId}", Todo.class, todoId); // 外部のREST APIの呼び出し
model.addAttribute(todo);
return "detail";
}
}
何も行わないと、外部のREST APIにアクセスしてしまいます。システムテストなどでは外部のREST APIに実際につなぐ必要がありますが、単体テストやモジュール結合テストでは外部のREST APIにつなぎたくないケースもあります。そういった場合には、MockRestServiceServer
が使えます。
@RunWith(SpringRunner.class)
@WebAppConfiguration
// ...
public class SpringTest {
@Autowired
WebApplicationContext wac;
MockMvc mockMvc;
@Autowired
RestTemplate restTemplate; // テスト対象のクラスで使うRestTemplateと同じものをインジェクションする
@Before
public void setUpMockMvc() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void detail() throws Exception {
// 外部のAPIを呼び出す際に使用するRestTemplateを、MockRestServiceServerと紐づける。
// こうすることで、MockRestServiceServerにリクエストが送られるようになる。
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
// 期待結果をMockRestServiceServerに登録する
mockServer.expect(requestTo("https://api.domain/todos/123"))
.andRespond(withSuccess("{\"id\":\"123\", \"title\":\"タイトル\", \"finished\":false}", MediaType.APPLICATION_JSON_UTF8));
mockMvc.perform(get("/todos/123"))
.andExpect(status().isOk())
.andExpect(view().name("detail"))
.andExpect(model().attribute("todo", hasProperty("id", is("123"))))
.andExpect(model().attribute("todo", hasProperty("title", is("タイトル"))))
.andExpect(model().attribute("todo", hasProperty("finished", is(false))));
mockServer.verify(); // 期待通りのリクエストがMockRestServiceServerに送信されたか検証する
}
}
さらに詳しい使い方を知りたい方は「Springの公式リファレンス」をご覧ください。
2016/6/6 追記
「SpringのRestTemplateを使うコンポーネントのJUnitテストはこう書く!!」にMockRestServiceServer
の仕組みと使い方をまとめました。
モックレスポンスを返却する回数の制御
で、Spring 4.3からexpect
メソッドの第一引数に任意のExpectedCount
オブジェクトを指定することで、指定したモックレスポンスを返却する回数を指定できるようになります。
// 地味に列挙する必要があったが・・・
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("https://api.domain/todos/123"))
.andRespond(withSuccess("{\"id\":\"123\", \"title\":\"タイトル\", \"finished\":false}",
mockServer.expect(requestTo("https://api.domain/todos/123"))
.andRespond(withSuccess("{\"id\":\"123\", \"title\":\"タイトル\", \"finished\":false}",
mockServer.expect(requestTo("https://api.domain/todos/123"))
.andRespond(withSuccess("{\"id\":\"123\", \"title\":\"タイトル\", \"finished\":false}",
// ...
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
// 回数指定が可能になる!!
mockServer.expect(times(3), requestTo("https://api.domain/todos/123"))
.andRespond(withSuccess("{\"id\":\"123\", \"title\":\"タイトル\", \"finished\":false}",
// ...
times
意外にも、once
, manyTimes
, min
, max
, between
メソッドが用意されています。
順序性の無視
Spring 4.3から、expect
メソッドを使って指定した期待結果(モックレスポンス)の順序性を検証対象から除外する(無視する)ことを示すフラグ(ignoreExpectOrder
)が追加されます。ignoreExpectOrder
をtrue
にすると、定義順に関係なくリクエスト内容に一致する期待結果(モックレスポンス)が消費されます。
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate)
.ignoreExpectOrder(true).build();
bindTo
メソッド
ちなみに・・・MockRestServiceServer
のbindTo
メソッドはSpring 4.3から追加されたメソッドで、MockRestServiceServer
用のビルダーオブジェクトを返却します。MockRestServiceServer
のオプションを変更する必要がなければ、MockRestServiceServer
のcreateServer
メソッドを使うこともできます。
MockRestServiceServer
でフォーム形式のリクエストボディの検証ができる
Spring 4.3から、クライアントサイド向けモックRESTサーバ機能(MockRestServiceServer
)にて、リクエストボディに指定したフォームデータ(application/x-www-form-urlencoded
形式)の検証ができるようになります。
たとえば、外部のWeb APIにフォームデータ(application/x-www-form-urlencoded
形式)を送信するような処理があったとします。
@RequestMapping("/todos")
@Controller
public class TodoController {
@Autowired
RestOperations restOperations;
@RequestMapping(method = RequestMethod.POST)
public String postTodo(Form from) {
// フォームデータ(application/x-www-form-urlencoded形式)を送信
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("title", from.getTitle());
restOperations.postForObject("https://api.domain/todos", formData, Void.class);
return "complete";
}
}
この処理に対するテストは、以下のように書くことができます。
@Test
public void postTodo() throws Exception {
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
MultiValueMap<String, String> expectedFormData = new LinkedMultiValueMap<>();
formData.add("title", "タイトル");
mockServer.expect(requestTo("https://api.domain/todos"))
.andExpect(content().contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andExpect(content().formData(expectedFormData)) // リクエストデータの期待値を指定
.andRespond(withCreatedEntity(URI.create("https://api.domain/todos/123")));
mockMvc.perform(post("/todos")
.param("title", "タイトル"))
.andExpect(status().isOk())
.andExpect(view().name("complete"));
mockServer.verify();
}
まとめ
今回は、テスト関連の主な変更点を紹介しました。今回でSpring Framework 4.3の変更点紹介シリーズはおわりですが、本シリーズで紹介していない変更点も数多くあります。その中には、本シリーズで紹介してものより利用頻度が多そう!?なものもあるので、興味がある方は是非「Spring JIRAのIssue」を確認してみてください。
いったん終了しますが、継続して更新していきたいと思います。
参考サイト
補足
Spring 4.3 GAに伴い変更点を追加 (2016/6/11)
ついに4.3がGAになり、そのタイミングで主な変更点に追加されたトピックスを反映しました。(「★6/11追加」でマークしてあります)