1. Introduction
REST Assured は、REST ベースのサービスに対するテストを簡単に記述できる、HTTP Builder で構築された Java 専用の DSL です。
さまざまな HTTP メソッド(POST、GET、PUT、DELETE、OPTIONS、PATCH、HEAD)のリクエストに対するレスポンスを確認、検証できます。
1.1. static imoprt
REST Assured を効率的に使うには、次のような import 文を記述するといいでしょう。
import static io.restassured.RestAssured.*;
import static io.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;
Json Schema のバリデーション機能を使うには、次のような import 文も追加するといいでしょう。
import static io.restassured.module.jsv.JsonSchemaValidator.*;
詳しくは Json Schema Validation のセクションを参照してください。
あなたのアプリケーションが Spring MVC を使っているなら、Spring Mock MVC モジュールを使えば、コントローラークラスのユニットテストを REST Assured の DSL で記述できます。
その場合、io.restassured.RestAssured
の代わりに RestAssuredMockMvc を import 文に記述します。
import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;
2. Examples
2.1. Example 1 - JSON
http:/localhost:8080/lotto
に対する GET リクエストが次のような JSON を返すことにします。
{
"lotto":{
"lottoId":5,
"winning-numbers":[2,45,34,23,7,5,3],
"winners":[
{
"winnerId":23,
"numbers":[2,45,34,23,3,5]
},
{
"winnerId":54,
"numbers":[52,3,12,11,18,22]
}
]
}
}
REST Assured で、GET リクエストに対するレスポンスの lottoId
が 5
になることを検証するときは次のように記述します。とても簡単です。
get("/lotto")
.then()
.body("lotto.lottoId", equalTo(5));
winnerId
に 23
と 24
が入っていることを確認するときは次のように記述します。
get("/lotto")
.then()
.body("lotto.winners.winnerId", hasItems(23, 54));
注意:equalTo
と hasItems
は Hamcrest ライブラリのマッチャーメソッドなので、org.hamcrest.Matchers
の import 文を追加する必要があります。
JSONPath の記法として、 Jayway の JsonPath ではなく、 Groovy の GPath を使っているので、間違えないようにしてください。
2.1.1. Returning floats and doubles as BigDecimal
REST Assured では、設定により JSONPath で指定した json number の値を float
や double
ではなく BigDecimal
として処理できます。
次のような JSON ドキュメントがあることにしましょう。
{
"price": 12.12
}
初期設定では、price
の値は float
の 12.12
と比較できます。
get("/price")
.then()
.body("price", is(12.12f));
JsonConfig
オブジェクトを次のように設定すれば、全ての json number の値を BigDecimal
で比較できるようになります。
given()
.config(
RestAssured.config().jsonConfig(
jsonConfig().numberReturnType(BIG_DECIMAL)))
.when()
.get("/price")
.then()
.body("price", is(new BigDecimal(12.12));
2.1.2. JSON Schema validation
REST Assured はバージョン 2.1.0
から Json Schema のバリデーションに対応しました。
例えば、次のようなスキーマを記述した products-schema.json
がクラスパスにあることにしましょう。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product set",
"type": "array",
"items": {
"title": "Product",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "number"
},
"name": {
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"dimensions": {
"type": "object",
"properties": {
"length": {"type": "number"},
"width": {"type": "number"},
"height": {"type": "number"}
},
"required": ["length", "width", "height"]
},
"warehouseLocation": {
"description": "Coordinates of the warehouse with the product",
"$ref": "http://json-schema.org/geo"
}
},
"required": ["id", "name", "price"]
}
}
次のように記述すると、JSON スキーマで、リソース /products
から取得した JSON ドキュメントを検証できます。
get("/products")
.then()
.assertThat()
.body(matchesJsonSchemaInClasspath("products-schema.json"));
matchesJsonSchemaInClasspath
は io.restassured.module.jsv.JsonSchemaValidator
のクラスメソッドです。
全てのクラスメソッドを static import しておくことをお勧めします。
この機能を使用するには、 json-schema-validator
モジュールを Maven の依存ライブラリへ追加しなければなりません。
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>4.4.0</version>
</dependency>
2.1.3. JSON Schema Validation Settings
REST Assured の json-schema-validator
モジュールは、Francis Galiegue の json-schema-validator ライブラリ(以降は fge
と呼びます)を使用してバリデーションを行います。
fge
ライブラリの設定を変更するときは次のように記述してください。
// Given
JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.newBuilder()
.setValidationConfiguration(
ValidationConfiguration.newBuilder()
.setDefaultVersion(DRAFTV4).freeze())
.freeze();
// When
get("/products")
.then()
.assertThat()
.body(matchesJsonSchemaInClasspath("products-schema.json")
.using(jsonSchemaFactory));
バリデーションに使用する jsonSchemaFactory
のインスタンスは using
メソッドで指定できます。
バリデーションの内容をより細かく調整したいときに使用してください。
fge
ライブラリは checked
と unchecked
の2種類のバリデーションを使用できます。
初期設定では checked
バリデーションを使用しますが、unchecked
バリデーションを使用したいときは JsonSchemaValidatorSettings のインスタンスを構成してください。
get("/products")
.then()
.assertThat()
.body(matchesJsonSchemaInClasspath("products-schema.json")
.using(settings().with().checkedValidation(false)));
settings
は JsonSchemaValidatorSettings
のクラスメソッドです。
2.1.4. Json Schema Validation with static configuration
常に unchecked
バリデーションを使用すると同時に、JSON スキーマのバージョン 3 を使用するようにしたい場合は、毎回設定する代わりに、クラスメンバ変数で設定できるようになっています。
JsonSchemaValidator.settings = settings().with().jsonSchemaFactory(
JsonSchemaFactory.newBuilder().setValidationConfiguration(
ValidationConfiguration.newBuilder()
.setDefaultVersion(DRAFTV3).freeze())
.freeze())
.and()
.with()
.checkedValidation(false);
get("/products")
.then()
.assertThat()
.body(matchesJsonSchemaInClasspath("products-schema.json"));
JsonSchemaValidator のクラスメンバ変数 settings
を変更すると、JsonSchemaValidator
からインポートした全ての matcher
メソッドが DRAFTV3
バージョンと unchecked
バリデーションを使うようになります。
JsonSchemaValidator
を初期設定へ戻すには rest
メソッドを呼び出します。
JsonSchemaValidator.reset();
2.1.5. Json Schema Validation without REST Assured
json-schema-validator
モジュールは単独で使用することもできます。
JSON ドキュメントが String
になっているなら次のように記述できます。
import org.junit.Test;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
import static org.hamcrest.MatcherAssert.assertThat;
public class JsonSchemaValidatorWithoutRestAssuredTest {
@Test public void
validates_schema_in_classpath() {
// Given
String json = ... // Greeting response
// Then
assertThat(json, matchesJsonSchemaInClasspath("greeting-schema.json"));
}
}
詳しくは REST Assured 入門 を参照してください。
2.1.6. Anonymous JSON root validation
JSON ドキュメントの中には名前付きのルート属性がない場合もあります。
例えば、次のコードは正しい JSON ドキュメントです。
[1, 2, 3]
JSONPath に $
あるいは空文字列を指定すると、匿名のルート要素を確認できます。
例えば http:/localhost:8080/json
が前のような JSON ドキュメントを返すとしたら、次のように検証できます。
when()
.get("/json")
.then()
.body("$", hasItems(1, 2, 3));
// An empty string "" would work as well
// .body("", hasItems(1, 2, 3));
2.2. Example 2 - XML
XML ドキュメントも同じように確認できます。
http://localhost:8080/greetXML
に対する POST リクエストが次のようなレスポンスを返すことにしましょう。
<greeting>
<firstName>{params("firstName")}</firstName>
<lastName>{params("lastName")}</lastName>
</greeting>
注意:このURLで公開しているのは、リクエストパラメータで送信した firstName
と lastName
をそのまま送り返すサービスだと考えてください。
リクエストパラメータで送信した firstName
に対応するレスポンスは次のように検証できます。
given()
.parameters("firstName", "John", "lastName", "Doe")
.when()
.post("/greetXML")
.then()
.body("greeting.firstName", equalTo("John"));
firstName
と lastName
を両方とも検証するときは次のように記述します。
given()
.parameters("firstName", "John", "lastName", "Doe")
.when()
.post("/greetXML")
.then()
.body("greeting.firstName", equalTo("John"))
.body("greeting.lastName", equalTo("Doe"));
省略記法も使用できます。
with()
.parameters("firstName", "John", "lastName", "Doe")
.when()
.post("/greetXML")
.then()
.body("greeting.firstName", equalTo("John"),
"greeting.lastName", equalTo("Doe"));
フィールドを指定する記法について詳しくは Groovy の GPath を参照してください。
2.2.1. XML namespaces
レスポンス本文の期待値に名前空間を指定するときは、 io.restassured.config.XmlConfig で名前空間を宣言しなければなりません。
具体例として、http://localhost:8080
で提供しているリソース namespace-example
が次のような XML ドキュメントを返すことにします。
<foo xmlns:ns="http://localhost/">
<bar>sudo </bar>
<ns:bar>make me a sandwich!</ns:bar>
</foo>
URI http://localhost/
を名前空間として宣言し、XML ドキュメントを検証するには、次のように記述します。
given()
.config(
RestAssured.config().xmlConfig(
xmlConfig()
.declareNamespace("test", "http://localhost/")))
.when()
.get("/namespace-example")
.then()
.body("foo.bar.text()", equalTo("sudo make me a sandwich!"))
.body(":foo.:bar.text()", equalTo("sudo "))
.body("foo.test:bar.text()", equalTo("make me a sandwich!"));
この例で使用しているパス記法は、Groovy の XmlSlurper 記法です(2.6.0
より前のバージョンでは XmlSlurper 記法を使えません)。
2.6.0
より前のバージョンで使用できる記法を知りたければ リリースノートの後方互換性に関する記述 を参照してください。
2.2.2. XPath
XML ドキュメントは XPath で確認することもできます。
given()
.parameters("firstName", "John", "lastName", "Doe")
.when()
.post("/greetXML")
.then()
.body(hasXPath("/greeting/firstName", containsString("Jo")));
別の書き方も出来ます。
given()
.parameters("firstName", "John", "lastName", "Doe")
.when()
.post("/greetXML")
.then()
.body(hasXPath("/greeting/firstName[text()='John']"));
XPath で名前空間を使用するときは、XmlConfig
で有効化しなければなりません。
given()
.config(
RestAssured.config().xmlConfig(
xmlConfig()
.with()
.namespaceAware(true)))
.when()
.get("/package-db-xml")
.then()
.body(hasXPath("/db:package-database", namespaceContext));
namespaceContext
は javax.xml.namespace.NamespaceContext のインスタンスです。
2.2.3. Schema and DTD validation
XML ドキュメントの検証に XML スキーマ(XSD)や DTD を使うこともできます。
XSD example
get("/carRecords")
.then()
.assertThat()
.body(matchesXsd(xsd));
DTD example
get("/videos")
.then()
.assertThat()
.body(matchesDtd(dtd));
matchesXsd
や matchesDtd
は io.restassured.matcher.RestAssuredMatchers のクラスメソッドで、Hamcrest マッチャーです。
2.3. Example 3 - Complex parsing and validation
REST Assured が特に際立つ使い方を説明しましょう。
REST Assured は Groovy で実装しているので、Groovy のコレクション API の素晴らしいところがそのまま利点になっています。
Groovy の例を見てみましょう。
def words = ['ant', 'buffalo', 'cat', 'dinosaur']
def wordsWithSizeGreaterThanFour = words.findAll { it.length() > 4 }
1行目は単語(文字列)のリストを定義しているだけですが、2行目はちょっと面白いことをしています。
単語のリストから4文字以上の単語を探索するため、findAll
メソッドに4文字以上が真になる式をクロージャで渡しているのです。
クロージャの内部では暗黙的な変数 it
がリストの要素になります。
新しいリスト wordsWithSizeGreaterThanFour
には buffalo
と dinosaur
が含まれることになります。
Groovy のコレクションには、他にもいろんなメソッドがあります。
-
find
— クロージャの式が真になる最初の要素を探索します -
collect
— それぞれの要素についてクロージャを評価した結果を返します -
sum
— コレクションの全ての要素を加算します -
max
/min
— コレクションの全ての要素について最大あるいは最小の要素を返します
JSON ドキュメントや XML ドキュメントをバリデーションするのに、これらの API がどのように使われているのか説明しましょう。
2.3.1. XML Example
http://localhost:8080/shopping
が次のような XML ドキュメントを返すことにします。
<shopping>
<category type="groceries">
<item>Chocolate</item>
<item>Coffee</item>
</category>
<category type="supplies">
<item>Paper</item>
<item quantity="4">Pens</item>
</category>
<category type="present">
<item when="Aug 10">Kathryn's Birthday</item>
</category>
</shopping>
このような XML ドキュメントについて、種類(type)が食料品(groceries)、内容(items)にチョコレート(Chocolate)とコーヒー(Coffee)を含むカテゴリー(category)があることを確かめるには、次のように記述します。
when()
.get("/shopping")
.then()
.body("shopping.category.find { it.@type == 'groceries' }.item", hasItems("Chocolate", "Coffee"));
詳しく見ていきましょう。
XPath に指定した shopping.category
はカテゴリーのリストを返します。
そして、カテゴリーのリストに対する find
は type
属性が groceries
に一致する最初のカテゴリーを返します。
それから、発見したカテゴリーに関連付けられた全ての item
を返します。
最終的に1つ以上の item
からなるリストを hasItems
マッチャーで検証します。
item
を取得するだけで、検証したいわけではないとしたらどうすればいいでしょうか。
そういうときは XmlPath を使用します。
// Get the response body as a String
String response = get("/shopping").asString();
// And get the groceries from the response. "from" is statically imported from the XmlPath class
List<String> groceries = from(response).getList("shopping.category.find { it.@type == 'groceries' }.item");
レスポンス本文について、食料品(groceries)のリストだけを処理したい場合は ショートカット することもできます。
// Get the response body as a String
List<String> groceries = get("/shopping").path("shopping.category.find { it.@type == 'groceries' }.item");
Depth-first search
前に説明した内容は次のような省略記法でも実現できます。
when()
.get("/shopping")
.then()
.body("**.find { it.@type == 'groceries' }", hasItems("Chocolate", "Coffee"));
*\*
は XML ドキュメントについて深さ優先探索をする省略記法です。
type
属性が groceries
に一致する最初のノードを検索できます。
XPath に item
を書いてないことに注目してください。
それぞれのカテゴリーノードについて item
のリストを返すとき、自動的に toString()
メソッドが呼び出されるからです。
2.3.2. JSON Example
http://localhost:8080/store
が次のような JSON ドキュメントを返すことにします。
{
"store":{
"book":[
{
"author":"Nigel Rees",
"category":"reference",
"price":8.95,
"title":"Sayings of the Century"
},
{
"author":"Evelyn Waugh",
"category":"fiction",
"price":12.99,
"title":"Sword of Honour"
},
{
"author":"Herman Melville",
"category":"fiction",
"isbn":"0-553-21311-3",
"price":8.99,
"title":"Moby Dick"
},
{
"author":"J. R. R. Tolkien",
"category":"fiction",
"isbn":"0-395-19395-8",
"price":22.99,
"title":"The Lord of the Rings"
}
]
}
}
Example 1
1つ目の例は、/store
にリクエストを送信し、レスポンスについて価格(price)が10未満の本(book)の書名(title)が "Sayings of the Century" と "Moby Dick" であることを検証する場合です。
when()
.get("/store")
.then()
.body("store.book.findAll { it.price < 10 }.title", hasItems("Sayings of the Century", "Moby Dick"));
XML ドキュメントと同様に、価格が10未満なら真になる式を評価するクロージャで、全ての本を探索します。
そして hasItems
マッチャーで期待値と比較します。
JsonPath を使うと title
のリストを取得できます。
// Get the response body as a String
String response = get("/store").asString();
// And get all books with price < 10 from the response. "from" is statically imported from the JsonPath class
List<String> bookTitles = from(response).getList("store.book.findAll { it.price < 10 }.title");
Example 2
2つ目の例として、著者名の文字列長の合計が50を越えるかどうかを検証してみましょう。
少し複雑な目的のため、Groovy のコレクションとクロージャの組み合わせの強力さを説明するちょうどいい例になっているでしょう。
when()
.get("/store")
.then()
.body("store.book.author.collect { it.length() }.sum()", greaterThan(50));
store.book.author
は全ての著者名のリストになります。
そして著者名のリストに collect
メソッドでクロージャ { it.length() }
を適用し、著者名の文字列長のリストを取得します。
それから文字列長のリストに sum()
メソッドを呼び出して、文字列長の合計を計算します。
最後に greaterThan
マッチャーで文字列長の合計が 53
を越えるか検証します。
実はこの記述も省略できます。
"word" の例で説明します。
def words = ['ant', 'buffalo', 'cat', 'dinosaur']
Groovy では展開演算子(spread operator) \*
でリストのそれぞれの要素に関数を呼び出すことができます。
def words = ['ant', 'buffalo', 'cat', 'dinosaur']
assert [3, 6, 3, 8] == words*.length()
展開演算子を使うと、前のコード例は次のように記述できます。
when()
.get("/store")
.then()
.body("store.book.author*.length().sum()", greaterThan(50));
もちろん、 JsonPath で値を取得することもできます。
// Get the response body as a string
String response = get("/store").asString();
// Get the sum of all author length's as an int. "from" is again statically imported from the JsonPath class
int sumOfAllAuthorLengths = from(response).getInt("store.book.author*.length().sum()");
// We can also assert that the sum is equal to 53 as expected.
assertThat(sumOfAllAuthorLengths, is(53));
2.4. Deserialization with Generics
REST Assured 3.3.0
では io.restassured.mapper.TypeRef
クラスを導入しました。
これは、レスポンスを総称型のコンテナへ分解するために使用します。
例えば、/products
に対する GET リクエストが次のような JSON ドキュメントを返すことにしましょう。
[
{
"id": 2,
"name": "An ice sculpture",
"price": 12.50,
"tags": ["cold", "ice"],
"dimensions": {
"length": 7.0,
"width": 12.0,
"height": 9.5
},
"warehouseLocation": {
"latitude": -78.75,
"longitude": 20.4
}
},
{
"id": 3,
"name": "A blue mouse",
"price": 25.50,
"dimensions": {
"length": 3.1,
"width": 1.0,
"height": 1.0
},
"warehouseLocation": {
"latitude": 54.4,
"longitude": -32.7
}
}
]
TypeRef
を使うと、このドキュメントのルート要素を List<Map<String, Object>>
へ変換できます(これ以外にも任意の総称型コンテナへ変換できます)。
// Extract
List<Map<String, Object>> products = get("/products").as(new TypeRef<List<Map<String, Object>>>() {});
// Now you can do validations on the extracted objects:
assertThat(products, hasSize(2));
assertThat(products.get(0).get("id"), equalTo(2));
assertThat(products.get(0).get("name"), equalTo("An ice sculpture"));
assertThat(products.get(0).get("price"), equalTo(12.5));
assertThat(products.get(1).get("id"), equalTo(3));
assertThat(products.get(1).get("name"), equalTo("A blue mouse"));
assertThat(products.get(1).get("price"), equalTo(25.5));```
この機能は今のところ JSON ドキュメントにしか使用できないので注意してください。
2.5. Additional Examples
Micha Kops がhttp://www.hascode.com/2011/10/testing-restful-web-services-made-easy-using-the-rest-assured-framework/[さまざまな具体例を説明するよいブログ記事]を書いています。
また、 Bas Dijkstra は自身の主催している REST Assured の活用ワークショップをオープンソースとして GitHub リポジトリで惜しみなく公開してくれているので、誰でも試すことができるし、誰でも貢献できるようになっています。
Bas Dijkstra は、他にも REST Assured を紹介する素敵なスクリーンキャストを公開しています。
2.6. Note on floats and doubles
浮動小数点数は Java の基本型の float
と比較しなければなりません。
例えば、次のような JSON オブジェクトがあることにしましょう。
{
"price": 12.12
}
the following test will fail, because we compare with a "double" instead of a "float":
次のテストは、float
じゃなくて double
で比較しているため失敗してします。
get("/price").then().assertThat().body("price", equalTo(12.12));
float
と比較すれば成功します。
get("/price").then().assertThat().body("price", equalTo(12.12f));
2.7. Note on syntax
REST Assured のブログ記事に登場するコード例では given/expect/when
という記法がたくさん使われています。
given()
.param("x", "y")
.expect()
.body("lotto.lottoId", equalTo(5))
.when()
.get("/lotto");
これは「レガシー記法」と呼ばれるもので、REST Assured 1.x でテストを記述するための、事実上の標準になっている記法でした。
最新バージョンでも正常に動作するので、何が正しいかを巡って多くのユーザーを混乱させています。
最初に given/when/then
記法を使わなかったのは純粋に技術的な問題があったからです。
REST Assured 2.0 が出るまで given/when/then
記法に対応しなかったのは、BDD スタイルのテストとして標準的な方法とは言えない状況だったからです。
その頃は given/expect/when
記法がそれなりに存在感を示していたのですが、しばらくすると given/when/then
記法の分かりやすさが認知され始め、大半の場合に使われる記法になっていきました。
given/expect/when
記法には given/when/then
記法にない良いところがあります。
それは、期待値に対する全てのエラーを同時に表示できるところです(最後に期待値を書く記法だとできない)。
例えば、前のコード例にステータスコードの期待値を追加したら次のようになります。
given()
.param("x", "y")
.expect()
.statusCode(400)
.body("lotto.lottoId", equalTo(6))
.when()
.get("/lotto");
この場合、REST Assured はステータスコードとレスポンス本文それぞれの期待値との不一致を報告します。
これを given/when/then
記法で書いてみましょう。
given()
.param("x", "y")
.when()
.get("/lotto")
.then()
.statusCode(400)
.body("lotto.lottoId", equalTo(6));
この場合、REST Assured はステータスコードの期待値との不一致だけを報告します。
2つ目の期待値を確認するには、もう1度テストを実行しなければならないのです。
2.7.1. Syntactic Sugar
REST Assured を使う上で、いろいろ用意されている糖衣構文のことを説明しておくべきでしょう。
例えば、複数の要素を1行で記述するとき、and
メソッドがあると可読性が高まります。
given().param("x", "y").and().header("z", "w").when().get("/something").then().assertThat().statusCode(200).and().body("x.y", equalTo("z"));
これは次のように記述したのと同じです。
given()
.param("x", "y")
.header("z", "w")
.when()
.get("/something")
.then()
.statusCode(200)
.body("x.y", equalTo("z"));
3. Getting Response Data
レスポンス本文を取得できます。
例えば、/lotto
への GET リクエストに対して、次のようにレスポンス本文を取得できます。
InputStream stream = get("/lotto").asInputStream(); // Don't forget to close this one when you're done
byte[] byteArray = get("/lotto").asByteArray();
String json = get("/lotto").asString();
3.1. Extracting values from the Response after validation
レスポンスを検証した後でも、extract
メソッドでレスポンスのインスタンスやレスポンス本文を取得できます。
後続のリクエストで使いたい値がレスポンスに含まれているときは便利な機能です。
例えば、/title
リソースが次のような JSON ドキュメントを返すことにしましょう。
{
"title" : "My Title",
"_links": {
"self": { "href": "/title" },
"next": { "href": "/title?page=2" }
}
}
コンテンツタイプが JSON
であることと、title
が My Title
であることを確認してから、次にリクエストを送信するためのリンクを next
から取得しなければならないとします。
その場合は次のように記述できます。
String nextTitleLink =
given()
.param("param_name", "param_value")
.when()
.get("/title")
.then()
.contentType(JSON)
.body("title", equalTo("My Title"))
.extract()
.path("_links.next.href");
get(nextTitleLink). ..
複数の値を抽出しなければならない場合はレスポンス全体のインスタンスを取得することもできます。
Response response =
given().
.param("param_name", "param_value")
.when()
.get("/title")
.then()
.contentType(JSON)
.body("title", equalTo("My Title"))
.extract()
.response();
String nextTitleLink = response.path("_links.next.href");
String headerValue = response.header("headerName");
3.2. JSON (using JsonPath)
レスポンス本文があれば JsonPath で自由に値を取得できます。
int lottoId = from(json).getInt("lotto.lottoId");
List<Integer> winnerIds = from(json).get("lotto.winners.winnerId");
こちらの書き方のほうが効率的です。
JsonPath jsonPath = new JsonPath(json).setRoot("lotto");
int lottoId = jsonPath.getInt("lottoId");
List<Integer> winnerIds = jsonPath.get("winners.winnderId");
JsonPath
は REST Assured が無くても使用できます。
詳しくは REST Assured 入門 を参照してください。
3.2.1. JsonPath Configuration
JsonPath
がオブジェクトをデシリアライズする振る舞いは変更できます。
JsonPath jsonPath = new JsonPath(SOME_JSON).using(new JsonPathConfig("UTF-8"));
JsonPath
の全てのインスタンスが共有するクラスメンバ変数で全体の設定を変更することもできます。
JsonPath.config = new JsonPathConfig("UTF-8");
JsonPath
について詳しくは Jayway のブログ記事 を参照してください。
ただし、JsonPath
の実装はhttps://github.com/jayway/JsonPath[Jayway の JsonPath] ではなく、 Groovy の GPath を使っているので、間違えないようにしてください。
3.3. XML (using XmlPath)
XmlPath を使うと REST Assured と同じような操作ができます。
String xml = post("/greetXML?firstName=John&lastName=Doe").andReturn().asString();
// Now use XmlPath to get the first and last name
String firstName = from(xml).get("greeting.firstName");
String lastName = from(xml).get("greeting.firstName");
// or a bit more efficiently:
XmlPath xmlPath = new XmlPath(xml).setRoot("greeting");
String firstName = xmlPath.get("firstName");
String lastName = xmlPath.get("lastName");
XmlPath
は REST Assured が無くても使用できます。
詳しくは REST Assured 入門 を参照してください。
3.3.1. XmlPath Configuration
XmlPath
がオブジェクトをデシリアライズする振る舞いは変更できます。
XmlPath xmlPath = new XmlPath(SOME_XML).using(new XmlPathConfig("UTF-8"));
XmlPath
の全てのインスタンスが共有するクラスメンバ変数で全体の設定を変更することもできます。
XmlPath.config = new XmlPathConfig("UTF-8");
XmlPath
について詳しくは Jayway のブログ記事 を参照してください。
3.3.2. Parsing HTML with XmlPath
XmlPath
で HTML互換モード を設定すると、XmlPath
の記法(GPath
)で HTML ページをパースできるようになります。
例えば、次のような HTML ドキュメントについて title
要素を取得したい場合について考えてみましょう。
<html>
<head>
<title>my title</title>
</head>
<body>
<p>paragraph 1</p>
<br>
<p>paragraph 2</p>
</body>
</html>
XmlPath
は次のように設定しておきましょう。
String html = ...
XmlPath xmlPath = new XmlPath(CompatibilityMode.HTML, html);
そうすると、title
要素は次のように取得できます。
xmlPath.getString("html.head.title"); // will return "mytitle"
この例を実行するには io.restassured.path.xml.XmlPath.CompatibilityMode.HTML
を static import しておく必要があります。
3.4. Single path
1つのリクエストに対して1つのパスだけが必要な場合は、次のような省略記法が利用できます。
int lottoId = get("/lotto").path("lotto.lottoid");
REST Assured はレスポンスのコンテンツタイプから JsonPath
と XmlPath
のどちらを使用するのか自動的に判断します。
コンテンツタイプが存在しない場合は既定のパーサーの設定を参照します。
どちらを使用するのか明示的に指定することもできます。
String firstName = post("/greetXML?firstName=John&lastName=Doe").andReturn().xmlPath().getString("firstName");
指定できる選択肢は xmlPath
と jsonPath
と htmlPath
です。
3.5. Headers, cookies, status etc
HTTP ヘッダーや HTTP Cookie、ステータスラインやステータスコードを取得できます。
Response response = get("/lotto");
// Get all headers
Headers allHeaders = response.getHeaders();
// Get a single header value:
String headerName = response.getHeader("headerName");
// Get all cookies as simple name-value pairs
Map<String, String> allCookies = response.getCookies();
// Get a single cookie value:
String cookieValue = response.getCookie("cookieName");
// Get status line
String statusLine = response.getStatusLine();
// Get status code
int statusCode = response.getStatusCode();
3.6. Multi-value headers and cookies
A header and a cookie can contain several values for the same name.
HTTP ヘッダーや HTTP Cookie は同じキーに複数の値を含む場合があります。
3.6.1. Multi-value headers
HTTP ヘッダーの全ての値を取得するには、最初に Response から Headers を取得します。
Headers
のインスタンスメソッド Headers.getValues(<header name>) は全ての値の List
を返します。
3.6.2. Multi-value cookies
HTTP ヘッダーの全ての値を取得するには、最初に Response から Cookies を取得します。
Cookies
のインスタンスメソッド Cookies.getValues(<header name>) は全ての値の List
を返します。
3.7. Detailed Cookies
Response.getDetailedCookie(java.lang.String) メソッドを使うと、コメント、パス、有効期限等の詳細な情報を全て含む、指定した名前の HTTP Cookie を取得できます。
Response.getDetailedCookies() メソッドを使うと、コメント、パス、有効期限等の詳細な情報を全て含む、全ての HTTP Cookie を取得できます。
4. Specifying Request Data
リクエストオブジェクトには、リクエストパラメータと共に HTTP ヘッダーや HTTP Cookie、リクエストボディやコンテンツタイプを指定できます。
4.1. Invoking HTTP resources
基本的には リクエストの仕様 に定義された "HTTP リクエストメソッド" を呼び出すことでリクエストを送信できるようになっています。
when().get("/x"). ..;
この get
は HTTP リクエストメソッドです。
REST Assured 3.0.0 からは、 request メソッドに指定した任意の HTTP 述語でリクエストを送信できるようになりました。
when()
.request("CONNECT", "/somewhere")
.then()
.statusCode(200);
このコード例では CONNECT
リクエストをサーバーに送信します。
4.2. Parameters
リクエストパラメータは次のように指定します。
given()
.param("param1", "value1")
.param("param2", "value2")
.when()
.get("/something");
REST Assured は HTTP リクエストメソッドに応じて、リクエストパラメータの種類(クエリパラメータやフォームパラメータ)を自動的に判断します。
この例では GET リクエストなのでクエリパラメータになります。
POST リクエストの場合はフォームパラメータになります。
PUT リクエストや POST リクエストではクエリパラメータをフォームパラメータを使い分ける必要があるため、次のように記述します。
given()
.formParam("formParamName", "value1")
.queryParam("queryParamName", "value2")
.when()
.post("/something");
URL に直接クエリパラメータを埋め込むこともできます。
.when()
.get("/name?firstName=John&lastName=Doe");
マルチパート形式のパラメータを送信する方法は マルチパートフォームデータ を参照してください。
4.2.1. Multi-value parameter
同じパラメータ名で複数の値を送信する場合は、それぞれの値を可変長引数として指定します。
given().param("myList", "value1", "value2"). ..
リストでも指定できます。
List<String> values = new ArrayList<String>();
values.add("value1");
values.add("value2");
given().param("myList", values). ..
4.2.2. No-value parameter
パラメータ名だけを指定すると値を送信しないようにできます。
given().param("paramName"). ..
4.2.3. Path parameters
リクエストを送信するURLにパスパラメータを埋め込むことができます。
post("/reserve/{hotelId}/{roomNumber}", "My Hotel", 23);
REST Assured では前のコード例のような使い方を "名前無しパスパラメータ" と呼び、添え字で参照します(最初のプレースホルダは "My Hotel"
なので hotelId
には "My Hotel" が入ります)。
名前有りパスパラメータもあります。
given()
.pathParam("hotelId", "My Hotel")
.pathParam("roomNumber", 23)
.when().
.post("/reserve/{hotelId}/{roomNumber}")
.then()
..
パスパラメータを使うとリクエストURLを読みやすくなるし、複数のテストで再利用しやすくなります。
REST Assured 2.8.0 から、名前無しパスパラメータと名前有りパスパラメータを混在して利用できるようになりました。
given().
pathParam("hotelId", "My Hotel").
when().
post("/reserve/{hotelId}/{roomNumber}", 23).
then().
..
この場合、rototNumber
は 23
になります。
パスパラメータの数と、パラメータに指定した値の数が一致しないとエラーになります。
filterでは、パスパラメータの追加や変更や削除(冗長なパラメータの削除等)を伴うより高度な使い方を説明しています。
4.3. Cookies
HTTP Cookie を指定する最も簡単なやり方は次のとおりです。
given().cookie("username", "John").when().get("/cookie").then().body(equalTo("username"));
複数の HTTP Cookie を指定することもできます。
given().cookie("cookieName", "value1", "value2"). ..
前のコード例は2つの HTTP Cookie を作成します(cookieName=value1
と cookieName=value2
)。
HTTP Cookie の詳細情報を指定するときは次のように記述します。
Cookie someCookie = new Cookie.Builder("some_cookie", "some_value").setSecured(true).setComment("some comment").build();
given().cookie(someCookie).when().get("/cookie").then().assertThat().body(equalTo("x"));
複数の HTTP Cookie を同時に指定できます。
Cookie cookie1 = Cookie.Builder("username", "John").setComment("comment 1").build();
Cookie cookie2 = Cookie.Builder("token", 1234).setComment("comment 2").build();
Cookies cookies = new Cookies(cookie1, cookie2);
given().cookies(cookies).when().get("/cookie").then().body(equalTo("username, token"));
4.4. Headers
given().header("MyHeader", "Something").and(). ..
given().headers("MyHeader", "Something", "MyOtherHeader", "SomethingElse").and(). ..
HTTP ヘッダーには複数の値を設定できます。
given().header("headerName", "value1", "value2"). ..
前のコード例は2つの HTTP ヘッダーを作成します(headerName=value1
と headerName=value2
)。
Header Merging/Overwriting
By default headers are merged. So for example if you do like this:
初期設定では指定された HTTP ヘッダーはマージするようになっています。
例えば、次のように記述した場合、HTTP リクエストには2つの HTTP ヘッダー(x: 1
と x: 2
)が含まれることになります。
given().header("x", "1").header("x", "2"). ..
HTTP ヘッダーをマージするかどうかは HeaderConfig で変更できます。
この場合送信する HTTP ヘッダーとして送信するのは x: 2
です。
given().
config(RestAssuredConfig.config().headerConfig(headerConfig().overwriteHeadersWithName("x"))).
header("x", "1").
header("x", "2").
when().
get("/something").
...
4.5. Content Type
given().contentType(ContentType.TEXT). ..
given().contentType("application/json"). ..
4.6. Request Body
given().body("some body"). .. // Works for POST, PUT and DELETE requests
given().request().body("some body"). .. // More explicit (optional)
given().body(new byte[]{42}). .. // Works for POST, PUT and DELETE
given().request().body(new byte[]{42}). .. // More explicit (optional)
serialization では Java オブジェクトを JSON ドキュメントや XML ドキュメントへシリアライズする方法を説明しています。
5. Verifying Response Data
HTTP レスポンスについて検証できるのは、ステータスコードやステータスライン、HTTP Cookie、HTTP ヘッダー、コンテンツタイプ、レスポンス本文です。
5.1. Response Body
JSON ドキュメント や XML ドキュメント の具体例を参照してください。
レスポンス本文を Java オブジェクトに写像することもできます。
詳しくは deserialization を参照してください。
5.2. Cookies
get("/x").then().assertThat().cookie("cookieName", "cookieValue"). ..
get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", "cookieValue2"). ..
get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", containsString("Value2")). ..
5.3. Status
get("/x").then().assertThat().statusCode(200). ..
get("/x").then().assertThat().statusLine("something"). ..
get("/x").then().assertThat().statusLine(containsString("some")). ..
5.4. Headers
get("/x").then().assertThat().header("headerName", "headerValue"). ..
get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", "headerValue2"). ..
get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", containsString("Value2")). ..
HTTP ヘッダーを検証するときは写像関数も利用できます。
例えば、Content-Length
ヘッダーの値が 1000
未満であることを確認するなら、ヘッダーの値を int
へ変換し、Integer
として Hamcrest
マッチャーに渡すようにできます。
get("/something").then().assertThat().header("Content-Length", Integer::parseInt, lessThan(1000));
5.5. Content-Type
get("/x").then().assertThat().contentType(ContentType.JSON). ..
5.6. Full body/content matching
get("/x").then().assertThat().body(equalTo("something")). ..
5.7. Use the response to verify other parts of the response
You can use data from the response to verify another part of the response. For example consider the following JSON document returned from service x:
レスポンスから取得したデータを、レスポンスの他の部分の検証に使うことができます。
例えばあるサービスが次のような JSON ドキュメントを返したことにしましょう。
{ "userId" : "some-id", "href" : "http://localhost:8080/some-id" }
このとき、"href" 属性の値が "userId" 属性の値で終端しているか確かめたいことにします。
そういうときは、io.restassured.matcher.ResponseAwareMatcher
で次のように記述します。
get("/x").then().body("href", new ResponseAwareMatcher<Response>() {
public Matcher<?> matcher(Response response) {
return equalTo("http://localhost:8080/" + response.path("userId"));
}
});
Java 8 を使っているなら代わりにラムダ式を使うことが出来ます。
get("/x").then().body("href", response -> equalTo("http://localhost:8080/" + response.path("userId")));
io.restassured.matcher.RestAssuredMatchers
には定義済みのいろいろなマッチャーがあります。
Spring Mock MVC モジュールを使っているなら io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers
を使いましょう。
get("/x").then().body("href", endsWithPath("userId"));
ResponseAwareMatchers
のマッチャーメソッドは、他のマッチャーメソッドや Hamcrest
のマッチャーメソッドと組み合わせることができます。
get("/x").then().body("href", and(startsWith("http:/localhost:8080/"), endsWithPath("userId")));
and
メソッドは io.restassured.matcher.ResponseAwareMatcherComposer
のクラスメソッドです。
5.8. Measuring Response Time
REST Assured 2.8.0 から応答時間を計測できるようになりました。
long timeInMs = get("/lotto").time()
時間単位を指定することもできます。
long timeInSeconds = get("/lotto").timeIn(SECONDS);
SECONDS
は TimeUnit
の列挙値です。
応答時間の計測にバリデーション DSL を組み合わせることもできます。
when().
get("/lotto").
then().
time(lessThan(2000L)); // Milliseconds
時間単位も指定できます。
when().
get("/lotto").
then().
time(lessThan(2L), SECONDS);
応答時間の計測は JVM が十分に暖まっている状態で行うようにしてください(単発のテストで計測しても不正確な結果しか得られません)。
また、計測値はサーバーの処理時間を厳密に保証するものではありません(HTTP の往復時間や REST Assured の処理時間などを含むため)。
6. Authentication
REST Assured はさまざまな認証方式に対応しています。
具体的には OAuth、Digest認証、証明書、フォーム認証、プリエンプティブベーシック認証等です。
認証方式はそれぞれのリクエストに指定できます。
given().auth().basic("username", "password"). ..
全てのリクエストで使用する認証方式を指定することもできます。
RestAssured.authentication = basic("username", "password");
他にも、リクエストやレスポンスの仕様 として指定することができます。
6.1. Basic Authentication
ベーシック認証には2種類あります。
1つは「プリエンプティブベーシック認証」、もう1つは「チャレンジベーシック認証」です。
6.1.1. Preemptive Basic Authentication
余計な接続をする負担を減らすため、特定の時点でサーバーが 404(Unauthorized) レスポンスステータスを返す前に、資格情報を送信する方式です。
サーバーがチャレンジを処理する能力をテストする場合を除いて、ほとんどの場面で使われることになります。
given().auth().preemptive().basic("username", "password").when().get("/secured/hello").then().statusCode(200);
6.1.2. Challenged Basic Authentication
「チャレンジベーシック認証」を使う場合、サーバーが明示的に要求しない限り、REST Assured は資格情報を提供しません。
つまり、REST Assured は最初にチャレンジのためのリクエストを送信してから、資格情報を HTTP ヘッダーに設定したリクエストを送信するのです。
given().auth().basic("username", "password").when().get("/secured/hello").then().statusCode(200);
6.2. Digest Authentication
今のところ「チャレンジダイジェスト認証」にだけ対応しています。
given().auth().digest("username", "password").when().get("/secured"). ..
6.3. Form Authentication
フォーム認証 はインターネットで非常に広く使われている方式です。
ユーザーはWebページで資格情報(ユーザー名とパスワード)を入力し、ログインボタンで送信します。
フォーム認証のための必要最小限のWebページは次のようになるでしょう。
<html>
<head>
<title>Login</title>
</head>
<body>
<form action="j_spring_security_check" method="POST">
<table>
<tr><td>User: </td><td><input type='text' name='j_username'></td></tr>
<tr><td>Password:</td><td><input type='password' name='j_password'></td></tr>
<tr><td colspan='2'><input name="submit" type="submit"/></td></tr>
</table>
</form>
</body>
</html>
サーバーは、ユーザーが "j_username" フィールドと "j_password" フィールトを入力して "submit" ボタンをクリックすることを期待しています。
フォーム認証で保護されたサービスを REST Assured でテストするには次のように記述します。
given().
.auth().form("John", "Doe")
.when()
.get("/formAuth")
.then()
.statusCode(200);
最適な動作をするわけではありません。
このように記述した場合、REST Assured はログインページを取得するための余計なリクエストを送信するからです。
REST Assured は取得したWebページを解析し、ユーザー名とパスワードに対応する入力フィールドと、フォームアクションURIを探索します。
Webページの複雑さによって解析に失敗する場合もあります。
フォーム認証を構成するときは、あらかじめ解析して得られるはずの情報を指定したほうがよいです。
例えば次のように記述します。
given()
.auth()
.form("John", "Doe", new FormAuthConfig("/j_spring_security_check", "j_username", "j_password"))
.when()
.get("/formAuth")
.then()
.statusCode(200);
この場合 REST Assured はログインページを取得し、解析するためのリクエストを送信する必要がありません。
Spring Security が初期設定で構成されているなら、FormAuthConfig
に定義済みの springSecurity
を使うことができます。
given()
.auth().form("John", "Doe", FormAuthConfig.springSecurity())
.when()
.get("/formAuth")
.then()
.statusCode(200);
6.3.1. CSRF
サーバーのレスポンスに CSRF 対策のためのトークン値を含めるのが一般的になっています。
REST Assured はサーバーから受信した CSRF トークンを自動的に解釈するようになっていますが、そのためには 必ず Webページ(Webサイトの一部)を取得するリクエストを送信しなければなりません。
CSRF 機能は次のように有効化します。
given()
.auth().form("John", "Doe", formAuthConfig().withAutoDetectionOfCsrf())
.when()
.get("/formAuth")
.then()
.statusCode(200);
この場合、REST Assured は自動的にWebページを解析してCSRFトークンを探索します。
より正確にCSRFトークンを探索できるようにするため、REST Assured にフィールド名を指定することができます。
次のコード例は、Spring Security が初期設定で構成されているものとして、FormAuthConfig
に定義済みの springSecurity
を使用し、"\_csrf" で終わるフィールド名を CSRF トークンの設定されたフィールドとして探索させています。
given()
.auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf"))
.when()
.get("/formAuth")
.then()
.statusCode(200);
初期設定では、CSFR トークンをフォームパラメータとして送信するようになっています。
ただし、必要に応じて HTTP ヘッダーとして送信するように設定することができます。
given()
.auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf").sendCsrfTokenAsHeader())
.when()
.get("/formAuth")
.then()
.statusCode(200);
6.3.2. Include additional fields in Form Authentication
REST Assured 3.1.0 からフォーム認証へ入力フィールドを追加できるようになりました。
FormAuthConfig
に追加するフィールドを指定するだけです。
例えば、次のようなWebページがあるとします。
<html>
<head>
<title>Login</title>
</head>
<body>
<form action="/login" method="POST">
<table>
<tr>
<td>User: </td>
<td><input type="text" name="j_username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="j_password"></td>
</tr>
<tr>
<td colspan="2"><input name="submit" type="submit"/></td>
</tr>
</table>
<input type="hidden" name="firstInputField" value="value1"/>
<input type="hidden" name="secondInputField" value="value2"/>
</form>
</body>
</html>
そして、firstInputField
と secondInputField
を追加したいときは、次のように記述します。
given().auth().form("username", "password",
formAuthConfig().withAdditionalFields("firstInputField", "secondInputField")). ..
REST Assured は自動的にWebページを解析し、指定したフィールドを探索し、発見した値をログインリクエストのフォームパラメータへ設定します。
6.4. OAuth
OAuth 1 や OAuth 2(クエリパラメータに署名する)を使用するには、クラスパスに ScribeJava を追加しなければなりません。
あなたのプロジェクトが Maven を使っているなら次のような依存ライブラリを追加します。
Maven を使っていない場合は GitHub のリリース から取得した jar ファイルをクラスパスに配置します。
<dependency>
<groupId>com.github.scribejava</groupId>
<artifactId>scribejava-apis</artifactId>
<version>8.3.1</version>
<scope>test</scope>
</dependency>
6.4.1. OAuth 1
OAuth 1 を使用するにはクラスパスに ScribeJava が必要です。
認証処理は次のように記述します。
given().auth().oauth(..). ..
6.4.2. OAuth 2
REST Assured 2.5.0 から、OAuth 2 を使用するときに ScribeJava へ依存しなくなりました。
given().auth().oauth2(accessToken). ..
このように記述すると、OAuth 2 の accessToken
を HTTP ヘッダーに埋め込むことができます。
明示的に次のように記述することもできます。
given().auth().preemptive().oauth2(accessToken). ..
given().auth().oauth2(..)
という記法は後方互換性のために残されています(やっている内容は同じです)。
OAuth 2 トークンをクエリパラメータで送信させたいときは、クラスパスに ScribeJava を配置して、次のように記述します。
given().auth().oauth2(accessToken, OAuthSignature.QUERY_STRING). ..
6.5. Custom Authentication
REST Assured では独自の認証プロバイダーを使用できます。
そのためには、io.restassured.spi.AuthFilter
インターフェイスを実装したクラスを filter に指定します。
例えば、決して安全性を高める方法ではありませんが、あなたのサービスが独自の HTTP ヘッダー "AUTH" に、2種類の HTTP ヘッダーの値を組み合わせた値を要求するなら、次のように記述します。
given()
.filter((requestSpec, responseSpec, ctx) -> {
String header1 = requestSpec.getHeaders().getValue("header1");
String header2 = requestSpec.getHeaders().getValue("header2");
requestSpec.header("AUTH", header1 + header2);
return ctx.next(requestSpec, responseSpec);
})
.when()
.get("/customAuth")
.then()
.statusCode(200);
Filter
ではなく AuthFilter
を使用するべき理由は、given().auth().none(). ..
のように記述した場合、AuthFilters
が自動的に除去してくれるからです。
7. Multi-part form data
巨大なデータをサーバーに送信するときは、マルチパート形式で送信するのが一般的です。
REST Assured では multiPart
メソッドを使って、ファイルやバイト配列、入力ストリームや文字列を送信できます。
例えば、次のように記述すると簡単にファイルをアップロードできます。
given()
.multiPart(new File("/path/to/file"))
when()
.post("/upload");
このコード例では、マルチパート形式のデータ構造において、あるコンテンツに対応する部分の名前が "file" になります。
HTML では input タグの name 属性が使われます。
次のような HTML について考えてみましょう。
<form id="uploadForm" action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" size="40">
<input type=submit value="Upload!">
</form>
この場合、input タグの name 属性が "file" なので、あるコンテンツに対応する部分の名前は "file" になります。
別の名前にするときは次のように記述します。
given()
.multiPart("controlName", new File("/path/to/file"))
.when()
.post("/upload");
同じリクエストで複数のコンテンツを送信するときは次のように記述します。
byte[] someData = ..
given()
.multiPart("controlName1", new File("/path/to/file"))
.multiPart("controlName2", "my_file_name.txt", someData)
.multiPart("controlName3", someJavaObject, "application/json")
.when()
.post("/upload");
より複雑な制御が必要なときは MultiPartSpecBuilder を使います。
Greeting greeting = new Greeting();
greeting.setFirstName("John");
greeting.setLastName("Doe");
given()
.multiPart(new MultiPartSpecBuilder(greeting, ObjectMapperType.JACKSON_2)
.fileName("greeting.json")
.controlName("text")
.mimeType("application/vnd.custom+json").build())
.when()
.post("/multipart/json")
.then()
.statusCode(200);
特に、コンテンツに対応する名前の初期値やファイル名を変更したい場合は MultiPartConfig を構成します。
given().config(config().multiPartConfig(multiPartConfig().defaultControlName("something-else"))). ..
このコード例では、コンテンツに対応する名前が "file" ではなく "something-else" になります。
より詳しい内容は JayWay のブログ記事 を参照してください。
8. Object Mapping
REST Assured は Java オブジェクトと JSON および XML を相互変換できるようになっています。
JSON を処理するにはクラスパスに Jackson や Jackson2、Gson や Johnzon の jar ファイルを配置しなければなりません。
また、XML を処理するには JAXB が必要です。
8.1. Serialization
次のような Java オブジェクトを JSON にシリアライズして送信する方法がいろいろあります。
public class Message {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
8.1.1. Content-Type based Serialization
Message message = new Message();
message.setMessage("My messagee");
given()
.contentType("application/json")
.body(message)
.when()
.post("/message");
このコード例では、リクエストに指定した HTTP ヘッダー content-type の値である "application/json" に従って、オブジェクトを JSON にシリアライズします。
最初はクラスパスから Jackson を探索し、無ければ Gson を使います。
content-type の値を "application/xml" にすると、JAXB を使用して XML へシリアライズします。
content-type を指定しなかった場合は、次の順にシリアライズを試みます。
-
JSON using Jackson 2 (Faster Jackson (databind))
-
JSON using Jackson (databind)
-
JSON using Gson
-
JSON using Johnzon
-
JSON-B using Eclipse Yasson
-
XML using JAXB
REST Assured は content-type に含まれる charset も考慮します。
Message message = new Message();
message.setMessage("My messagee");
given()
.contentType("application/json; charset=UTF-16")
.body(message)
.when()
.post("/message");
Message
クラスのインスタンスをフォームパラメータへシリアライズすることもできます。
Message message = new Message();
message.setMessage("My messagee");
given()
.contentType("application/json; charset=UTF-16")
.formParam("param1", message)
.when()
.post("/message");
このコード例では、文字エンコーディングを UTF-16 として、Jackson (databind) あるいは Gson で JSON へシリアライズします。
8.1.2. Create JSON from a HashMap
Map のインスタンスから JSON ドキュメントを作成できます。
Map<String, Object> jsonAsMap = new HashMap<>();
jsonAsMap.put("firstName", "John");
jsonAsMap.put("lastName", "Doe");
given()
.contentType(JSON)
.body(jsonAsMap)
.when()
.post("/somewhere")
.then()
.statusCode(200);
このコード例は、次のような JSON ドキュメントをリクエスト本文として送信します。
{ "firstName" : "John", "lastName" : "Doe" }
8.1.3. Using an Explicit Serializer
クラスパスに複数のオブジェクトマッピングライブラリが存在する場合や、content-type の値を無視させたい場合は、明示的にシリアライザーを指定します。
Message message = new Message();
message.setMessage("My messagee");
given()
.body(message, ObjectMapperType.JAXB)
.when()
.post("/message");
このコード例では message オブジェクトを JAXB で XML へシリアライズします。
8.2. Deserialization
レスポンス本文を、Java オブジェクトにデシリアライズする方法もいろいろあります。
public class Message {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
8.2.1. Content-Type based Deserialization
サーバーから次のような JSON ドキュメントを受信したことにします。
{"message":"My message"}
Message クラスのオブジェクトへデシリアライズするには次のように記述します。
Message message = get("/message").as(Message.class);
この場合、受信した HTTP ヘッダー content-type の値は "application/json" でなければなりません("json" を含む値ならなんでも構いません)。
一方、サーバーから次のような XML ドキュメントを受信したことにします。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
<message>My message</message>
</message>
この場合、受信した HTTP ヘッダー content-type の値は "application/xml" でなければなりません。
同じコードのままで動作します。
Message message = get("/message").as(Message.class);
Custom Content-Type Deserialization
サーバーから受信した content-type の値が "application/something" のように独自の値でも、いろいろな方法で Java オブジェクトへデシリアライズさせることができます。
具体的には、明示的に指定する方法や、独自の content-type に対応するパーサーを登録する方法があります。
content-type に対応するパーサーを登録するには次のように記述します。
Message message = expect().parser("application/something", Parser.XML).when().get("/message").as(Message.class);
Message message = expect().defaultParser(Parser.XML).when().get("/message").as(Message.class);
8.2.2. Using an Explicit Deserializer
クラスパスに複数のオブジェクトマッピングライブラリが存在する場合や、content-type の値を無視させたい場合は、明示的にデシリアライザーを指定します。
Message message = get("/message").as(Message.class, ObjectMapperType.GSON);
8.3. Configuration
既定のオブジェクトマッパーは ObjectMapperConfig で設定できます。
また、ObjectMapperConfig
は 詳細設定 へ指定できます。
例えば、Gson のフィールド名規則を小文字とアンダースコアで構成するには次のように記述します。
RestAssured.config = RestAssuredConfig.config().objectMapperConfig(objectMapperConfig().gsonObjectMapperFactory(
new GsonObjectMapperFactory() {
public Gson create(Class cls, String charset) {
return new GsonBuilder().setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
}
}
));
REST Assured は Gson、JAXB、Jackson、Jackson2、Eclipse Yasson(JSON-B)のための定義済みファクトリクラスを提供しています。
8.4. Custom
REST Assured はクラスパスを探索して利用可能なオブジェクトマッパーを判断します。
未対応のオブジェクトマッパーを利用したい場合は、 io.restassured.mapper.ObjectMapper の独自実装を、body
メソッドの第二引数へ指定します。
given().body(myJavaObject, myObjectMapper).when().post("..")
クラスメンバ変数へ設定すれば、全ての呼び出しから利用させることができます。
RestAssured.config = RestAssuredConfig.config().objectMapperConfig(new ObjectMapperConfig(myObjectMapper));
具体例は ソースコード で確認してください。
9. Parsers
9.1. Custom
REST Assured は HTML、XML、JSON の定義済みパーサーを提供します。
また、REST Assured が未対応の content-type に対応するパーサーを登録することもできます。
RestAssured.registerParser(<content-type>, <parser>);
MIMEタイプ "application/vnd.uoml+xml" を XML パーサーで解析させるときは次のように記述します。
RestAssured.registerParser("application/vnd.uoml+xml", Parser.XML);
登録したパーサーは解除できます。
RestAssured.unregisterParser("application/vnd.uoml+xml");
リクエストごとにパーサーを指定することもできます。
get(..).then().using().parser("application/vnd.uoml+xml", Parser.XML). ..;
レスポンスの仕様 として指定することもできます。
9.2. Default
レスポンスに HTTP ヘッダーの content-type が無い場合などのために、既定のパーサーを登録しておくと便利です。
RestAssured.defaultParser = Parser.JSON;
リクエストごとに既定のパーサーを指定することもできます。
get("/x").then().using().defaultParser(Parser.JSON). ..
レスポンスの仕様 として指定することもできます。
10. Default values
初期設定の REST Assured はリクエストを localhost の 8080 番ポートに送信します。
リクエストを送信するポート番号を変更したい場合は次のように記述します。
given().port(80). ..
単純にポート番号を含むURLを指定できます。
..when().get("http://myhost.org:80/doSomething");
全てのリクエストで使用する URI、パス、ポート番号、認証方式を変更できます。
RestAssured.baseURI = "http://myhost.org";
RestAssured.port = 80;
RestAssured.basePath = "/resource";
RestAssured.authentication = basic("username", "password");
RestAssured.rootPath = "x.y.z";
この場合、get("/hello")
というコードは、"username" と "password" を資格情報とするベーシック認証で http://myhost.org:80/resource/hello
に GET リクエストを送信します。
rootPath
について詳しくは ルートパス を参照してください。
他にもいろいろな初期値を変更できます。
RestAssured.filters(..); // フィルターのリスト
RestAssured.requestSpecification = .. // リクエストの仕様
RestAssured.responseSpecification = .. // レスポンスの仕様
RestAssured.urlEncodingEnabled = .. // リクエストパラメータの URL エンコードの有効化
RestAssured.defaultParser = .. // 未対応の content-type に対するレスポンス本文のパーサー
RestAssured.registerParser(..) // content-type に対応するパーサー
RestAssured.unregisterParser(..) // content-type から除外するパーサー
reset
メソッドは、それぞれの設定値を初期値に戻します(baseURI(localhost)、basePath(なし)、ポート番号(8080)、ルートパス(なし)、認証方式(なし)、URL エンコーディング(有効))。
RestAssured.reset();
11. Specification Re-use
いくつものテストで同じレスポンスの期待値やリクエストパラメータを重複させる代わりに、仕様として再利用できます。
リクエストの仕様は RequestSpecBuilder で、レスポンスの仕様は ResponseSpecBuilder で定義できます。
ステータスコードの期待値が 200、レスポンス本文の期待値が JSON 配列 x.y
の要素数が2であることとするレスポンスの仕様は、次のように記述します。
ResponseSpecBuilder builder = new ResponseSpecBuilder();
builder.expectStatusCode(200);
builder.expectBody("x.y.size()", is(2));
ResponseSpecification responseSpec = builder.build();
// "responseSpec" は他のテストでも再利用できます
when()
.get("/something")
.then()
.spec(responseSpec)
.body("x.y.z", equalTo("something"));
この例では、"responseSpec" として定義したレスポンス本文に関する期待値に、このテスト特有の期待値をマージすることになります。
全ての期待値を満たさなければテストは成功しません。
複数のテストで再利用するべきリクエストデータも同じように定義できます。
RequestSpecBuilder builder = new RequestSpecBuilder();
builder.addParam("parameter1", "parameterValue");
builder.addHeader("header1", "headerValue");
RequestSpecification requestSpec = builder.build();
given()
.spec(requestSpec)
.param("parameter2", "paramValue")
.when()
.get("/something")
.then()
.body("x.y.z", equalTo("something"));
この例でも "requestSpec" として定義した内容に、このテスト特有の内容をマージすることになります。
従って、このテストでは1つの HTTP ヘッダー "header1" と2つのリクエストパラメータ "parameter1" `"parameter2" を含むリクエストを送信することになります。
11.1. Querying RequestSpecification
RequestSpecification
に定義した値を参照したり抽出したりできると役に立つ場合があります。
そういう場合は io.restassured.specification.SpecificationQuerier
を使用します。
RequestSpecification spec = ...
QueryableRequestSpecification queryable = SpecificationQuerier.query(spec);
String headerValue = queryable.getHeaders().getValue("header");
String param = queryable.getFormParams().get("someparam");
12. Filters
フィルターを使うと、コミットする前のリクエストや、期待値と突き合わせて評価する前のリクエストを検証したり、変更したりできるようになります。
AOP(アスペクト指向プログラミング)における "around advice" のように考えることができます。
フィルターは、独自の認証方式やセッション管理、ログ出力を実現するためにも使用できます。
独自のフィルターを作成するときは io.restassured.filter.Filter を実装します。
given().filter(new MyFilter()). ..
REST Assured では次のようなフィルターを利用できるようになっています。
-
io.restassured.filter.log.RequestLoggingFilter
: リクエストの仕様について、詳細な内容を出力します -
io.restassured.filter.log.ResponseLoggingFilter
: 指定されたステータスコードの一致するレスポンスについて、詳細な内容を出力します。 -
io.restassured.filter.log.ErrorLoggingFilter
: エラーレスポンスを受信した場合(ステータスコードが 400 以上 500 未満の場合)、レスポンスの詳細な内容を出力します
Ordered Filters
REST Assured 3.0.2 から、 io.restassured.filter.OrderedFilter を実装することで、フィルターの適用順序を制御できるようになりました。
getOrder
メソッドの返す数値がフィルターの優先順位になります。
小さいほど優先順位が高くなります。
つまり、Integer.MIN_VALUE
の優先順位は最高、Inteer.MAX_VALUE
の優先順位は最低です。
OrderedFilter
を実装していないフィルターの優先順位は getOrder
メソッドが 1000
を返す場合と同じになります。
OrderedFilter
の使い方を知りたければ サンプルコード を参照してください。
Response Builder
フィルターで レスポンスオブジェクト を変更するときは、 ResponseBuilder で元のレスポンスオブジェクトから新しいオブジェクトを作成しなければなりません。
例えば、レスポンス本文を "something" に変更したいときは次のように記述します。
Response newResponse = new ResponseBuilder().clone(originalResponse).setBody("Something").build();
13. Logging
リクエストやレスポンスの詳細な内容を出力することが、正しい期待値や正しいリクエストを作成するのに役立つ場合があります。
そういう場合は REST Assured に組み込みのフィルターを使えば簡単です。
13.1. Request Logging
REST Assured 1.5 から、 RequestLoggingFilter で実際にサーバーへ送信する前にリクエストの仕様をログに出力できるようになりました。
HTTP Builder や HTTP Client の追加した HTTP ヘッダーも出力するので注意してください。
ただし、このフィルターは基本的にリクエストの仕様に定義した内容 だけ を出力するようになっています。
実際にサーバーへ送信するリクエストの内容そのものではないということです。
また、RequestLoggingFilter
より優先度の低いフィルターが変更した内容は出力できません。
サーバーに送信したリクエストと 完全に同じ内容 をログに出力したいときは、 HTTP Client のログ出力 を検討するか、 Wireshark などのツール利用を検討してください。
given().log().all(). .. // リクエストの仕様に定義したリクエストパラメータ、HTTP ヘッダー、リクエスト本文等、全てをログに出力します。
given().log().params(). .. // リクエストパラメータだけをログに出力します。
given().log().body(). .. // リクエスト本文だけをログに出力します。
given().log().headers(). .. // HTTP ヘッダーだけをログに出力します。
given().log().cookies(). .. // HTTP Cookie だけをログに出力します。
given().log().method(). .. // リクエストメソッドだけをログに出力します。
given().log().path(). .. // パスだけをログに出力します。
13.2. Response Logging
ステータスコードの値を無視してレスポンス本文をログに出力させたいときは次のように記述します。
get("/x").then().log().body() ..
このコード例では、たとえエラーレスポンスでもレスポンス本文をログに出力します。
エラーレスポンスの場合だけログに出力したい場合は次のように記述します。
get("/x").then().log().ifError(). ..
ステータスラインや HTTP ヘッダー、HTTP Cookie など全ての詳細な内容をログに出力したいときは次のように記述します。
get("/x").then().log().all(). ..
ステータスラインだけ、HTTP ヘッダーだけ、HTTP Cookie だけをログに出力したいときは次のように記述します。
get("/x").then().log().statusLine(). .. // ステータスラインだけをログに出力します。
get("/x").then().log().headers(). .. // HTTP ヘッダーだけをログに出力します。
get("/x").then().log().cookies(). .. // HTTP Cookie だけをログに出力します。
ステータスコードが指定した値に一致する、あるいは指定した条件と一致する場合だけログを出力したいときは次のように記述します。
get("/x").then().log().ifStatusCodeIsEqualTo(302). .. // ステータスコードが 302 のときだけログに出力します。
get("/x").then().log().ifStatusCodeMatches(matcher). .. // ステータスコードが Hamcrest マッチャーで真になったときだけログに出力します。
13.3. Log if validation fails
REST Assured 2.3.1 から、バリデーションに失敗した場合だけリクエストやレスポンスをログに出力できるようになりました。
リクエストをログに出力するときは次のように記述します。
given().log().ifValidationFails(). ..
レスポンスをログに出力するときは次のように記述します。
.. .then().log().ifValidationFails(). ..
リクエストとレスポンスのログ出力を同じように制御するには、 LogConfig を設定します。
given().config(RestAssured.config().logConfig(logConfig().enableLoggingOfRequestAndResponseIfValidationFails(HEADERS))). ..
このコード例では、バリデーションが失敗したとき、HTTP ヘッダーだけをログに出力します。
全てのテストについて、バリデーションが失敗したときリクエストとレスポンスをログに出力させるには、次のように記述します。
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
13.4. Blacklist Headers from Logging
REST Assured 4.2.0 から、リクエストやレスポンスをログに出力するとき、ログに出力させない HTTP ヘッダーの不許可リストを設定できるようになりました。
HTTP ヘッダーの値には、実際の値の代わりに [ BLACKLISTED ]
を出力するようになります。
LogConfig で設定できます。
given().config(config().logConfig(logConfig().blacklistHeader("Accept"))). ..
この場合、レスポンスは次のようにログへ出力されます。
Request method: GET Request URI: http://localhost:8080/something Proxy: <none> Request params: <none> Query params: <none> Form params: <none> Path params: <none> Headers: Accept=[ BLACKLISTED ] Cookies: <none> Multiparts: <none> Body: <none>
14. Root path
ルートパスを指定して、レスポンス本文の期待値に記述するパスの重複を減らすことができます。
ルートパスを指定しない場合は次のように記述します。
when()
.get("/something")
.then()
.body("x.y.firstName", is(..))
.body("x.y.lastName", is(..))
.body("x.y.age", is(..))
.body("x.y.gender", is(..));
ルートパスを指定すると次のように記述できます。
when()
.get("/something")
.then()
.root("x.y"). // "root" メソッドで指定します
.body("firstName", is(..))
.body("lastName", is(..))
.body("age", is(..))
.body("gender", is(..));
既定のルートパスを設定することもできます。
RestAssured.rootPath = "x.y";
既定のルートパスを延長できると、複雑なユースケースで役立つ場合があります。
ルートパスは appendRoot
メソッドで追加できます。
when()
.get("/jsonStore")
.then()
.root("store.%s", withArgs("book"))
.body("category.size()", equalTo(4))
.appendRoot("%s.%s", withArgs("author", "size()"))
.body(withNoArgs(), equalTo(4));
途中でルートパスを縮小することもできます。
when()
.get("/jsonStore")
.then()
.root("store.category")
.body("size()", equalTo(4))
.detachRoot("category")
.body("size()", equalTo(1));
15. Path arguments
定義済みの変数を組み合わせてパスを構成しているときは、パス引数形式にしたほうが便利な場合があります。
String someSubPath = "else";
int index = 1;
get("/x").then().body("something.%s[%d]", withArgs(someSubPath, index), equalTo("some value")). ..
このコード例では、レスポンス本文の something.else[0]
が "some value" と一致することを検証できます。
一部だけ異なる複雑な ルートパス の重複した記述を減らすために使うこともできます。
when()
.get("/x")
.then()
.root("filters.filterConfig[%d].filterConfigGroups.find { it.name == 'GroupName' }.includes")
.body(withArgs(0), hasItem("first"))
.body(withArgs(1), hasItem("second")).
..
パス引数の記法は Java の 書式付き出力 と同じです。
withArgs
メソッドを使うには io.restassured.RestAssured クラスの static imoprt が必要です。
ルートパスに記述された全ての引数がそろっているなら、引数無しでレスポンス本文を検証できるようにしたほうが便利でしょう。
そういうときは withNoArgs
メソッドを使います。
when()
.get("/jsonStore")
.then()
.root("store.%s", withArgs("book"))
.body("category.size()", equalTo(4))
.appendRoot("%s.%s", withArgs("author", "size()"))
.body(withNoArgs(), equalTo(4));
16. Session support
REST Assured は単純なセッション管理機能を提供しています。
セッションIDを指定するDSLは次のように記述します。
given().sessionId("1234"). ..
次のように記述した場合と同じことをしています。
given().cookie("JSESSIONID", "1234"). ..
全てのリクエストに指定するセッションIDの初期値をクラスメンバ変数に設定できます。
RestAssured.sessionId = "1234";
初期設定では HTTP Cookie の JSESSIONID
からセッションIDを取得します。
取得元は SessionConfig で変更できます。
RestAssured.config = RestAssured.config().sessionConfig(new SessionConfig().sessionIdName("phpsessionid"));
RequestSpecBuilder
でセッションIDを定義して、他のテストで再利用することもできます。
RequestSpecBuilder spec = new RequestSpecBuilder().setSessionId("value1").build();
// 最初のリクエストで送信するセッションIDは "value1"
given().spec(spec). ..
// 二回目のリクエストで送信するセッションIDも "value1"
given().spec(spec). ..
もちろん、レスポンスオブジェクトからセッションIDを取得できます。
String sessionId = get("/something").sessionId();
16.1. Session Filter
REST Assured 2.0.0 から、 SessionFilter でセッションIDの取得と送信を自動化できるようになりました。
SessionFilter sessionFilter = new SessionFilter();
given()
.auth().form("John", "Doe")
.filter(sessionFilter)
.when()
.get("/formAuth")
.then()
.statusCode(200);
given()
.filter(sessionFilter) // 直前のレスポンスから取得したセッションIDを再利用する
.when()
.get("/x")
.then()
.statusCode(200);
SessionFilter
の取得したセッションIDは次のように参照できます。
String sessionId = sessionFilter.getSessionId();
17. SSL
HTTP Builder や HTTP Client の提供する素晴らしい機能のおかげで、ほとんどの場合何もしなくても SSL を利用できるようになっています。
それでも、通信に失敗する場合はあります。
サーバーが不正な証明書を使っている場合は SSLPeerUnverifiedException
が発生します。
簡単な回避策は relaxedHTTPSValidation
で HTTPS のバリデーションを緩和することです。
given().relaxedHTTPSValidation().when().get("https://some_server.com"). ..
全てのリクエストについて設定することもできます。
RestAssured.useRelaxedHTTPSValidation();
リクエストの仕様 で設定することもできます。
次のコード例では SSLContext
のプロトコルとして SSL
を使用しています。
relaxedHTTPSValidation
のオーバーロードメソッドでは別のプロトコルを指定できるのです。
given().relaxedHTTPSValidation("TLS").when().get("https://some_server.com"). ..
自分で作成した Java キーストアファイルを使用するより詳細な設定もできるようになっています。
それほど難しいことではありませんが、 HTTP Builder の SSL に関する説明 を読んでおくといいでしょう。
given().keystore("/pathToJksInClassPath", <password>). ..
全てのリクエストについて設定することもできます。
RestAssured.keystore("/pathToJksInClassPath", <password>);
リクエストの仕様 で設定することもできます。
パスワード付きのキーストアを読み込んでからトラストストアへ設定することもできます。
RestAssured.trustStore(keystore);
使い方は サンプルコード を参照してください。
より高度な SSL の設定をするときは SSL の設定 を参照してください。
17.1. SSL invalid hostname
REST Assured 2.2.0 から、サーバー証明書のホスト名とアクセスするホスト名が一致しない場合でも、Java キーストアファイルを作成、インポートしなくてもよくなりました。
全てのリクエストについて設定するときは次のように記述します。
RestAssured.config = RestAssured.config().sslConfig(sslConfig().allowAllHostnames());
単独のリクエストについて設定するときは次のように記述します。
given().config(RestAssured.config().sslConfig(sslConfig().allowAllHostnames())). .. ;
relaxedHTTPSValidation
を有効化した場合、allowAllHostnames
も有効になっているので注意してください。
18. URL Encoding
REST Assured は自動的に URL エンコード処理をするので、基本的には考慮する必要がありません。
URL エンコード処理を無効にするときはそのための設定が必要です。
具体的には、URL エンコードされている値を REST Assured のパラメータとして渡す場合があるでしょう。
URL エンコードを二重に適用しないよう、REST Assured の URL エンコード処理を無効にしなければならないのです。
String response = given()
.urlEncodingEnabled(false)
.get("https://jira.atlassian.com:443/rest/api/2.0.alpha1/search?jql=project%20=%20BAM%20AND%20issuetype%20=%20Bug")
.asString();
..
全てのリクエストについて設定するときは次のように記述します。
RestAssured.baseURI = "https://jira.atlassian.com";
RestAssured.port = 443;
RestAssured.urlEncodingEnabled = false;
final String query = "project%20=%20BAM%20AND%20issuetype%20=%20Bug";
String response = get("/rest/api/2.0.alpha1/search?jql={q}", query);
..
19. Proxy Configuration
REST Assured 2.3.2 ではプロキシ対応機能を改善しました。
localhost の 8888 番ポートを指定するときは次のように記述します。
given().proxy("localhost", 8888). ..
プロキシホストが localhost の場合はポート番号を指定するだけでも動作します。
given().proxy(8888). .. // Will assume localhost
HTTPS でアクセスする必要があるときは、3番目の引数にプロトコルスキーマを指定するか、io.restassured.specification.ProxySpecification
を使用します。
given().proxy(host("localhost").withScheme("https")). ..
host
メソッドは io.restassured.specification.ProxySpecification
のクラスメソッドです。
REST Assured 2.7.0 から、プリエンプティブベーシック認証でプロキシに接続できるようになりました。
given().proxy(auth("username", "password")).when() ..
auth
メソッドは io.restassured.specification.ProxySpecification のクラスメソッドです。
host
メソッドで別のホスト名を指定できます。
given().proxy(host("http://myhost.org").withAuth("username", "password")). ..
19.1. Static Proxy Configuration
全てのリクエストについて設定するときは次のように記述します。
RestAssured.proxy("localhost", 8888);
次のように記述することもできます。
RestAssured.proxy = host("localhost").withPort(8888);
19.2. Request Specification Proxy Configuration
リクエストの仕様へプロキシ接続を設定することもできます。
RequestSpecification specification = new RequestSpecBuilder().setProxy("localhost").build();
given().spec(specification). ..
20. Detailed configuration
RestAssuredConfig で次のような内容を設定できます。
-
HTTP Client - HTTPClientConfig および RedirectConfig
-
Log - LogConfig
-
Encoder - EncoderConfig
-
Decoder - DecoderConfig
-
Session - SessionConfig
-
ObjectMapper - ObjectMapperConfig
-
Connection- ConnectionConfig
-
SSL- SSLConfig
-
Parameter- ParamConfig
単独のリクエストには次のように設定します。
given().config(RestAssured.config().redirect(redirectConfig().followRedirects(false))). ..
RequestSpecBuilder
には次のように設定します。
RequestSpecification spec = new RequestSpecBuilder().setConfig(
RestAssured.config().redirect(
redirectConfig().followRedirects(false)))
.build();
全てのリクエストを対象とするには次のように設定します。
RestAssured.config = config().redirect(
redirectConfig().followRedirects(true).and().maxRedirects(0));
config
メソッドや newConfig
メソッドは io.restassured.config.RestAssuredConfig
のクラスメソッドです。
20.1. Encoder Config
EncoderConfig では、コンテンツとクエリパラメータで使用する文字エンコーディングと文字セットを指定できます(指定しなかった場合は HTTP ヘッダー content-type の値に従います)。
コンテンツの文字セットを指定しなかった場合、ISO-8859-1
になります。
クエリパラメータの文字セットを指定しなかった場合、UTF-8
になります。
RestAssured.config = RestAssured.config().encoderConfig(
encoderConfig().defaultContentCharset("US-ASCII"));
EncoderConfig
の defaultCharsetForContentType
メソッドを使うと、content-type に文字セットが指定されていなかった場合に使用する文字セットのエンコーダを、content-type ごとに定義できます。
RestAssured.config = RestAssured.config(
config().encoderConfig(
encoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));
このコード例では、content-type の "application/xml" へ文字セットが明示的に指定されなかった場合、UTF-16
として扱います。
なお、"application/json" の既定の文字セットは RFC4627 で定義されており、UTF-8
として扱います。
20.1.1. Avoid adding the charset to content-type header automatically
REST Assured は自動的に文字セットのための HTTP ヘッダーを自動的に追加します。
この振る舞いを無効化するには EditorConfig
で設定します。
RestAssured.config = RestAssured.config(
config().encoderConfig(
encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false)));
20.2. Decoder Config
DecoderConfig では全てのレスポンスについて、コンテンツをデコードするときの既定の文字セットを設定できます。
既定の文字セットである ISO-8859-1
以外の文字セットを想定している場合や、レスポンスの content-type で文字セットが指定されていなかった場合に役立ちます。
RestAssured.config = RestAssured.config().decoderConfig(
decoderConfig().defaultContentCharset("UTF-8"));
特定のデコーダを指定することもできます。
この設定をすると、リクエストの HTTP ヘッダーに Accept-Encoding
を自動的に追加し、レスポンス本文も自動的にデコードします。
組み込みのデコーダとして GZIP
と DEFLATE
が利用できるようになっています。
GZIP
の代わりに DEFLATE
を使用する場合は次のように記述します。
given().config(RestAssured.config().decoderConfig(
decoderConfig().contentDecoders(DEFLATE))). ..
DecoderConfig
の defaultCharsetForContentType
メソッドを使うと、content-type に文字セットが指定されていなかった場合に使用する文字セットのデコーダーを、content-type ごとに定義できます。
RestAssured.config = RestAssured.config(
config().decoderConfig(
decoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));
このコード例では、content-type の "application/xml" へ文字セットが明示的に指定されなかった場合、UTF-16
として扱います。
なお、"application/json" の既定の文字セットは RFC4627 で定義されており、UTF-8
として扱います。
20.3. Session Config
SessionConfig
はセッションIDの受け渡しに使う既定のパラメータ名を設定できます。
初期値は JSESSIONID
です。
違う名前を使用するアプリケーションで セッション管理機能 を使いたい場合は設定しなければなりません。
RestAssured.config = RestAssured.config().sessionConfig(
new SessionConfig().sessionIdName("phpsessionid"));
20.4. Redirect DSL
リダイレクトの振る舞いは DSL で設定できます。
given().redirects().max(12).and().redirects().follow(true).when(). ..
20.5. Connection Config
REST Assured は HTTP 接続のための設定を変更できます。
例えば、Apache HTTP Client でレスポンスを受信するたびに強制的に HTTP 接続を閉じるようにできます。
小さいレスポンスを返すリクエストを、大量に、高速に、連続して送信したい場合などが該当します。
もちろん、いくつかの塊として(巨大な)データを受信しているときは、レスポンスを受信してすぐに HTTP 接続を閉じてはいけません。
初期設定ではレスポンスを受信するたびに HTTP 接続を閉じない ようになっています。
RestAssured.config = RestAssured.config().connectionConfig(c
onnectionConfig().closeIdleConnectionsAfterEachResponse());
20.6. Json Config
JsonPathConfig は REST Assured だけでなく、 JsonPath を単独で使用する場合の設定を変更できます。
次のコード例では JSON number の扱いを変更しています。
RestAssured.config = RestAssured.config().jsonConfig(
jsonConfig().numberReturnType(NumberReturnType.BIG_DECIMAL))
20.7. HTTP Client Config
REST Assured が HTTP リクエストを送信するために使用している HTTP Client インスタンスの設定を変更できます。
初期設定では given
メソッドが毎回新しいインスタンスを作成するようになっています。
作成したインスタンスを再利用できるようにするときは次のように記述します。
RestAssured.config = RestAssured.config().httpClient(
httpClientConfig().reuseHttpClientInstance());
httpClientFactory
メソッドで独自の HTTP Client インスタンスを指定することができます。
RestAssured.config = RestAssured.config().httpClient(
httpClientConfig().httpClientFactory(
new HttpClientConfig.HttpClientFactory() {
@Override
public HttpClient createHttpClient() {
return new SystemDefaultHttpClient();
}
}));
注意:今は AbstractHttpClient
のインスタンスしか指定できません。
もちろん、HTTP Client インスタンスの初期値を設定することもできます。
20.8. SSL Config
SSLConfig は、キーストアの種類やトラストストア、ホスト名検証器等、SSL の細かい設定を変更できます。
RestAssured.config = RestAssured.config().sslConfig(
sslConfig()
.with()
.keystoreType(<type>)
.and()
.strictHostnames());
20.9. Param Config
ParamConfig はパラメータの種類ごとに、パラメータ名が衝突した場合の処理を変更できます。
初期設定では全ての値をマージするようになっています。
次のコード例では、REST Assured の送信するクエリ文字列は param1=value1¶m1=value2
になります。
given().queryParam("param1", "value1").queryParam("param1", "value2").when().get("/x"). ...
後に指定した値で同じ名前のパラメータを 上書き したい場合は次のように記述します。
given()
.config(config().paramConfig(paramConfig().queryParamsUpdateStrategy(REPLACE)))
.queryParam("param1", "value1")
.queryParam("param1", "value2")
.when()
.get("/x"). ..
この場合、REST Assured は全てのパラメータをマージする代わりに、 param1
の値を value2
に上書きします(最後に指定した値です)。
パラメータの種類に応じて個別に設定するのではなく、全てのパラメータ種類で同じ処理をさせたいときは次のように記述します。
given().config(config().paramConfig(
paramConfig().replaceAllParameters())). ..
この機能は Spring Mock Mvc モジュール でも対応しています。
ただし、ParamConfig
の代わりに MockMvcParamConfig を使用します。
20.10. Failure Config
REST Assured 3.3.0 からは、バリデーションが失敗した場合に実行するコールバック処理を FailureConfig で指定できるようになりました。
リクエストやレスポンス、あるいはそれぞれの仕様に、独自のログ出力やデータ保存処理を追加するのに役立ちます。
例として、次のテストを実行してステータスコードが 200 じゃなかったら電子メールで通知させてみましょう。
given()
.param("x", "y")
.when()
.get("/hello")
.then()
statusCode(200);
ResponseValidationFailureListener を実装して FailureConfig
に指定します。
ResponseValidationFailureListener emailOnFailure = (reqSpec, respSpec, resp) -> emailService.sendEmail(
"email@gmail.com", "Important test failed! Status code was: " + resp.statusCode());
given()
.config(RestAssured.config().failureConfig(
failureConfig().with().failureListeners(emailOnFailure)))
.param("x", "y")
.when()
.get("/hello")
.then()
statusCode(200);
21. Spring Support
Spring MVC のコントローラを REST Assured の API でテストするため、2種類のモジュールを提供しています。
-
spring-mock-mvc - Spring MVC のコントローラのユニットテストに使用します。
-
spring-web-test-client - Spring Webflux のコントローラのユニットテストに使用します。
21.1. Spring Mock Mvc Module
REST Assured 2.2.0 から Spring Mock Mvc に対応する spring-mock-mvc
モジュールが使用できるようになりました。
つまり、Spring MVC のコントローラのユニットテストができるようになったのです。
@Controller
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping(value = "/greeting", method = GET)
public @ResponseBody Greeting greeting(
@RequestParam(value="name", required=false, defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
テストは RestAssuredMockMvc で記述します。
given()
.standaloneSetup(new GreetingController())
.param("name", "Johan")
.when()
.get("/greeting")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("content", equalTo("Hello, Johan!"));
REST Assured の基本的な記法そのままです。
すごい短い時間でテストを実行できるだけでなく、基本的な REST Assured に比べて、環境の準備や、(必要なら)モックを使用するのが簡単になっています。
基本的な REST Assured の準備に必要なことのほとんど(各種設定や仕様の準備、ロギング等)を RestAssuredMockMvc
がやってくれるからです。
Maven プロジェクトなら次のような依存ライブラリを追加します。
そうでなければ jarファイル をダウンロードしてクラスパスに配置します。
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
21.1.1. Bootstrapping RestAssuredMockMvc
次のような import 文を追加するだけです。
io.restassured.module.mockmvc.RestAssuredMockMvc.*
io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers.*
元の import 文は削除します。
io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
他に必要な import 文があるなら static import のセクションを参照してください。
RestAssuredMockMvc
のテストを実行するときは、先にコントローラや MockMvc
や WebApplicationContext
を初期化しておかなければなりません。
前のコード例で見たように、リクエストごとにもろもろの準備を実行できます。
given().standaloneSetup(new GreetingController()). ..
クラスメソッドで実行することもできます。
RestAssuredMockMvc.standaloneSetup(new GreetingController());
クラスメソッドで実行する場合、REST Assured の DSL でコントローラや MockMvc
や WebApplicationContext
のインスタンスを指定する必要はありません。
その場合、最初のコード例は次のように記述できます。
given()
.param("name", "Johan")
.when()
.get("/greeting")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("content", equalTo("Hello, Johan!"));
21.1.2. Asynchronous Requests
RestAssured
と RestAssuredMockMvc
は 2.5.0 からリクエストを非同期で送信できるようになりました。
@Controller
public class PostAsyncController {
@RequestMapping(value = "/stringBody", method = POST)
public @ResponseBody
Callable<String> stringBody(final @RequestBody String body) {
return new Callable<String>() {
public String call() throws Exception {
return body;
}
};
}
}
テストコードは次のように記述できます。
given()
.body("a string")
.when()
.async().post("/stringBody")
.then()
.body(equalTo("a string"));
これらのコードのタイムアウト時間は初期値の1秒になっています。
タイムアウト時間は DSL で指定できます。
given()
.body("a string")
.when()
.async().with().timeout(20, TimeUnit.SECONDS).post("/stringBody")
.then()
.body(equalTo("a string"));
AsyncConfig でもタイムアウト時間の初期値を設定できます。
given()
.config(config().asyncConfig(withTimeout(100, TimeUnit.MILLISECONDS)))
.body("a string")
.when()
.async().post("/stringBody")
.then()
.body(equalTo("a string"));
withTimeout
メソッドは io.restassured.module.spring.commons.config.AsyncConfig
のクラスメソッドで、タイムアウト時間を設定しただけの AsyncConfig
を生成します。
全てのリクエストでタイムアウト時間を変更する場合は次のように記述します。
リクエスト1とリクエスト2のどちらでも、タイムアウト時間は100ミリ秒になります。
RestAssuredMockMvc.config = RestAssuredMockMvc.config().asyncConfig(
withTimeout(100, TimeUnit.MILLISECONDS));
// リクエスト1
given()
.body("a string")
.when()
.async().post("/stringBody")
.then()
.body(equalTo("a string"));
// リクエスト2
given()
.body("another string")
.when()
.async().post("/stringBody")
.then()
.body(equalTo("a string"));
21.1.3. Adding Request Post Processors
Spring MockMvc は Request Post Processors を使用できますが、RestAssuredMockMvc
でも使用できます。
given().postProcessors(myPostProcessor1, myPostProcessor2). ..
可読性を高めるためにも、RequestPostProcessors
に org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
を指定する代わりに、auth
メソッドを使うようにしたほうがいいでしょう。
同じ振る舞いになるはずです。
given().auth().with(httpBasic("username", "password")). ..
httpBasic
メソッドは SecurityMockMvcRequestPostProcessor のクラスメソッドです。
21.1.4. Adding Result Handlers
Spring MockMvc は Result Handlers を使用できますが、RestAssuredMockMvc
でも使用できます。
MockMvc
の提供するログ出力機能を使用するときは次のように記述します。
.. .then().apply(print()). ..
print
メソッドは org.springframework.test.web.servlet.result.MockMvcResultHandlers
のクラスメソッドです。
REST Assured 2.6.0 より前のバージョンを使っている場合は代わりに resultHandlers
メソッドを使うようにしてください(resultHandlers
メソッドは 2.8.0 から非推奨になりました)。
given().resultHandlers(print()). ..
21.1.5. Using Result Matchers
Spring MockMvc は Result Matchers のさまざまな実装を提供しています。
中には役に立つものもあるでしょう。
RestAssuredMockMvc
でも同じように使用できます。
例えば、何らかの理由でステータスコードが 200 に一致するかどうか ResultMatcher
で判定しなければならないとしたら、次のように記述できます。
given()
.param("name", "Johan")
.when()
.get("/greeting")
.then()
.assertThat(status().isOk())
.body("id", equalTo(1))
.body("content", equalTo("Hello, Johan!"));
status
メソッドは org.springframework.test.web.servlet.result.MockMvcResultMatchers
のクラスメソッドです。
assertThat
メソッドの代わりに expect
メソッドを使えるので、そのほうが素の MockMvc
の記法に近づくでしょう。
21.1.6. Interceptors
MockHttpServletRequestBuilder を使えば実際に送信する前にリクエストの内容を変更できます。
そのためには、 MockHttpServletRequestBuilderInterceptor を実装して RestAssuredMockMvc
に指定します。
given().interceptor(myInterceptor). ..
21.1.7. Spring Mock Mvc Specifications
通常の REST Assured のようにリクエストやレスポンスの仕様を定義して再利用性を高めることができます。
RestAssuredMockMvc
では MockMvcRequestSpecBuilder でリクエストの仕様を作成しますが、レスポンスの仕様は ResponseSpecBuilder で作成します。
全てのリクエストやレスポンスで使用する仕様はクラスメンバ変数で変更できます。
RestAssuredMockMvc.requestSpecification = new MockMvcRequestSpecBuilder()
.addQueryParam("name", "Johan")
.build();
RestAssuredMockMvc.responseSpecification = new ResponseSpecBuilder()
.expectStatusCode(200)
.expectBody("content", equalTo("Hello, Johan!"))
.build();
given()
.standaloneSetup(new GreetingController())
.when()
.get("/greeting")
.then()
.body("id", equalTo(1));
21.1.8. Resetting RestAssuredMockMvc
reset
メソッドで RestAssuredMockMvc
の初期設定を復元できます。
21.2. Spring MVC Authentication
spring-mock-mvc
モジュールは 2.3.0 から認証機能に対応しました。
given().auth().principal(..). ..
一部の認証方式を使用するには Spring Security ライブラリをクラスパスに配置しなければなりません(任意です)。
認証方式はクラスメンバ変数で変更できます。
RestAssuredMockMvc.authentication = principal("username", "password");
principal
メソッドは RestAssuredMockMvc のクラスメソッドです。
リクエストビルダーで認証方式を変更することもできます。
MockMvcRequestSpecification spec = new MockMvcRequestSpecBuilder
.setAuth(principal("username", "password"))
.build();
21.2.1. Using Spring Security Test
spring-mock-mvc
モジュールは 2.5.0 で Spring Security の対応を改善しました。
クラスパスに spring-security-test
ライブラリがあるときは次のように記述できるようになります。
given().auth().with(httpBasic("username", "password")). ..
httpBasic
メソッドは SecurityMockMvcRequestPostProcessor のクラスメソッドです。
このメソッドはベーシック認証ができるようにします。
この機能を有効にするには MockMvc
のインスタンスに SecurityMockMvcConfigurer を指定しなければなりません。
直接指定する場合は次のように記述します。
MockMvc mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
RestAssuredMockMvc
に AbstractMockMvcBuilder のインスタンスを指定した場合、自動的に springSecurity
を構成します。
given()
.webAppContextSetup(context)
.auth().with(httpBasic("username", "password")). ..
完全なコード例は次のとおりです。
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfiguration.class)
@WebAppConfiguration
public class BasicAuthExample {
@Autowired
private WebApplicationContext context;
@Before public void
rest_assured_is_initialized_with_the_web_application_context_before_each_test() {
RestAssuredMockMvc.webAppContextSetup(context);
}
@After public void
rest_assured_is_reset_after_each_test() {
RestAssuredMockMvc.reset();
}
@Test public void
basic_auth_example() {
given().
auth().with(httpBasic("username", "password")).
when().
get("/secured/x").
then().
statusCode(200).
expect(authenticated().withUsername("username"));
}
}
全てのリクエストで使用する認証方式はクラスメンバで変更できます。
RestAssuredMockMvc.authentication = with(httpBasic("username", "password"));
with
メソッドは io.restassured.module.mockmvc.RestAssuredMockMvc
のクラスメソッドです。
リクエストの仕様で指定することもできます。
21.2.2. Injecting a User
Spring Security Test ライブラリの提供する @WithMockUser や @WithUserDetails といったアノテーションを使用できます。
@Controller
public class UserAwareController {
@RequestMapping(value = "/user-aware", method = GET)
public
@ResponseBody
String userAware(@AuthenticationPrincipal User user) {
if (user == null || !user.getUsername().equals("authorized_user")) {
throw new IllegalArgumentException("Not authorized");
}
return "Success";
}
}
userAware
メソッドの引数 User には @AuthenticationPrincipal が指定されているため、Spring Security がユーザーオブジェクトを注入します。
次のように記述すると、テスト用のユーザーを生成できます。
@WithMockUser(username = "authorized_user")
@Test public void
spring_security_mock_annotations_example() {
given()
.webAppContextSetup(context)
.when()
.get("/user-aware")
.then()
.statusCode(200)
.body(equalTo("Success"))
.expect(authenticated().withUsername("authorized_user"));
}
21.2.3. Spring Web Test Client Module
REST Assured 3.2.0 では Spring Reactive Web のテストに使用するコンポーネントとして spring-web-test-client
モジュールを導入しました。
このモジュールを使用すると Spring WebFlux のコントローラのユニットテストを作成できるようになります。
次のような DTO クラスを JSON ドキュメントとして返すコントローラがあることにしましょう。
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@GetMapping(value = "/greeting", produces = "application/json")
public Mono<Greeting> greeting(@RequestParam(value="name") String name) {
return Mono.just(new Greeting(counter.incrementAndGet(), String.format(template, name)));
}
}
RestAssuredWebTestClient を使うと次のように記述できます。
package io.restassured.module.webtestclient;
import io.restassured.module.webtestclient.setup.GreetingController;
import org.junit.Test;
import static io.restassured.module.webtestclient.RestAssuredWebTestClient.given;
public class GreetingControllerTest {
@Test
public void greeting_controller_returns_json_greeting() {
given()
.standaloneSetup(new GreetingController())
.param("name", "Johan")
.when()
.get("/greeting")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("content", equalTo("Hello, Johan!"));
}
}
REST Assured の基本的な記法そのままです。
すごい短い時間でテストを実行できるだけでなく、基本的な REST Assured に比べて、環境の準備や、(必要なら)モックを使用するのが簡単になっています。
基本的な REST Assured の準備に必要なことのほとんど(各種設定や仕様の準備、ロギング等)を RestAssuredWebTestClient
がやってくれるからです。
Maven プロジェクトなら spring-web-test-client
モジュールを依存ライブラリに追加します。
そうでなければ jarファイル をダウンロードしてクラスパスに配置します。
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-web-test-client</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
21.2.4. Bootstrapping RestAssuredWebTestClient
次のような import 文を追加するだけです。
io.restassured.module.webtestclient.RestAssuredWebTestClient.*
io.restassured.module.webtestclient.matcher.RestAssuredWebTestClientMatchers.*
元の import 文は削除します。
io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
他に必要な import 文があるなら static import のセクションを参照してください。
RestAssuredWebTestClient
のテストを実行するときは、先にコントローラや WebTestClient
や WebApplicationContext
を初期化しておかなければなりません。
前のコード例で見たように、リクエストごとにもろもろの準備を実行できます。
given().standaloneSetup(new GreetingController()). ..
クラスメソッドで実行することもできます。
RestAssuredWebTestClient.standaloneSetup(new GreetingController());
クラスメソッドで実行する場合、REST Assured の DSL でコントローラのインスタンスを指定する必要はありません。
その場合、最初のコード例は次のように記述できます。
given()
.param("name", "Johan")
.when()
.get("/greeting")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("content", equalTo("Hello, Johan!"));
21.2.5. Spring Web Test Client Specifications
通常の REST Assured のようにリクエストやレスポンスの仕様を定義して再利用性を高めることができます。
RestAssuredWebTestClient
では WebTestClientRequestSpecBuilder でリクエストの仕様を作成しますが、レスポンスの仕様は ResponseSpecBuilder で作成します。
全てのリクエストやレスポンスで使用する仕様はクラスメンバ変数で変更できます。
RestAssuredWebTestClient.requestSpecification = new WebTestClientRequestSpecBuilder()
.addQueryParam("name", "Johan")
.build();
RestAssuredWebTestClient.responseSpecification = new ResponseSpecBuilder()
.expectStatusCode(200)
.expectBody("content", equalTo("Hello, Johan!"))
.build();
given()
.standaloneSetup(new GreetingController())
.when()
.get("/greeting")
.then()
.body("id", equalTo(1));
21.2.6. Resetting RestAssuredWebTestClient
reset
メソッドで RestAssuredWebTestClient
の初期設定を復元できます。
21.3. Common Spring Module Documentation
21.3.1. Note on parameters
RestAssuredMockMvc
と RestAssuredWebTestClient
はどちらも使用できるパラメータは同じで、param
や formParam
や queryParam
は MockMvc
へ委譲するだけです。
通常の REST Assured と同様に、formParam
でパラメータを指定すると、自動的に HTTP ヘッダーの content-type へ application/x-www-form-urlencoded
を設定します。
22. Scala Support Module
REST Assured 2.6.0 で導入された scala-support モジュールは、 Response と MockMvcResponse の "then" メソッドに、別名の "Then" メソッドを追加します。
Scala では then
が予約語になっているため、Scala コンパイラが警告メッセージを出力するからです。
Then
メソッドを使用するには scala-support
モジュールの io.restassured.module.scala.RestAssuredSupport.AddThenToResponse
を import 文に追加します。
import io.restassured.RestAssured.when
import io.restassured.module.scala.RestAssuredSupport.AddThenToResponse
import org.hamcrest.Matchers.equalTo
import org.junit.Test
@Test
def `trying out rest assured in scala with implicit conversion`() {
when().
get("/greetJSON").
Then().
statusCode(200).
body("key", equalTo("value"))
}
spring-mock-mvc モジュール を使用する場合も同じようにします。
scala-support
モジュールを依存ライブラリに追加するには次のように記述します。
libraryDependencies += "io.rest-assured" % "scala-support" % "4.4.0"
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>scala-support</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
testImplementation 'io.rest-assured:scala-support:4.4.0'
No build manager:
クラスパスに jar ファイル を配置してください。
23. Kotlin
23.1. Avoid Escaping "when" Keyword
JetBrains の開発している Kotlin 言語はとても簡単に Java と統合できるので、REST Assured も簡単に利用できます。
ただし、1つだけ注意点があります。
Kotlin では when
が予約語になっているため、メソッド名をエスケープしなければならないのです。
@Test
fun `kotlin rest assured example`() {
given().
param("firstName", "Johan").
param("lastName", "Haleby").
`when`().
get("/greeting").
then().
statusCode(200).
body("greeting.firstName", equalTo("Johan")).
body("greeting.lastName", equalTo("Haleby"))
}
Kotlin Extension Module を使用すれば、簡単に 拡張メソッド を作成できるので、こちらの方がおすすめです。
例えば、when
メソッドの別名 When
メソッドを定義するには次のように記述します。
fun RequestSpecification.When(): RequestSpecification {
return this.`when`()
}
そうすると、前のコードは次のように記述できます。
@Test
fun `kotlin rest assured example`() {
given().
param("firstName", "Johan").
param("lastName", "Haleby").
When().
get("/greeting").
then().
statusCode(200).
body("greeting.firstName", equalTo("Johan")).
body("greeting.lastName", equalTo("Haleby"))
}
メソッド名のエスケープが不要になりました。
より詳しい内容は Johan Haleby のブログ記事 を参照してください。
23.2. Kotlin Extension Module
REST Assured 4.1.0 では kotlin-extensions
モジュールを導入しました。
このモジュールは Kotlin から REST Assured を使用するときに便利な拡張関数を提供します。
Maven プロジェクトなら次のような依存ライブラリを追加します。
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>kotlin-extensions</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
そして、io.restassured.module.kotlin.extensions
パッケージの Given
クラスを import 文に追加すると、次のように記述できます。
val message: String =
Given {
port(7000)
header("Header", "Header")
body("hello")
} When {
put("/the/path")
} Then {
statusCode(200)
body("message", equalTo("Another World"))
} Extract {
path("message")
}
このモジュールは Kotlin 開発者にとって使いやすい API を提供するだけでなく、Java API として利用する場合でも次のような利点があります。
-
失敗した期待値の評価をまとめて報告できるようになります
-
IDE でソースコードを整形してもきれいにインデントされます
拡張メソッドの名前は将来的に変更される可能性があります。
Kotlin 用の API に関する原則や利点については John Haleby のブログ記事 を参照してください。
23.3. Kotlin Extension Module for Spring MockMvc
REST Assured 4.1.0 では spring-mock-mvc モジュール の Kotlin 拡張に対応する spring-moc-mvc-kotlin-extensions
モジュールを導入しました。
次のように記述できます。
class RestAssuredMockMvcKotlinExtensionsTest {
@Test
fun example() {
val mockMvc =
MockMvcBuilders.standaloneSetup(GreetingController())
.build()
val id: Int =
Given {
mockMvc(mockMvc)
param("name", "Johan")
} When {
get("/greeting")
} Then {
body(
"id", Matchers.equalTo(1),
"content", Matchers.equalTo("Hello, Johan!")
)
} Extract {
path("id")
}
assertThat(id).isEqualTo(1)
}
Maven プロジェクトなら次のような依存ライブラリを追加します。
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc-kotlin-extensions</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
必要な拡張関数を io.restassured.module.mockmvc.kotlin.extensions
パッケージから import してください。
24. More info
For more information refer to the javadoc:
You can also have a look at some code examples:
-
REST Assured tests
If you need support then join the mailing list.
For professional support please contact johanhaleby.