Newer
Older
notes / REST / restassured.md

REST-assured

Rest-assuredとは

Rest-assuredは、RESTサービスの試験を行うためのテストフレームワークです。Java言語によるRESTサービスのテストは、RubyやGroovyなどの動的な言語を使用した場合と比べて難しくなりがちですが、REST-assuredを使用することで、それらの動的言語のシンプルさをJava言語でも使用することができます。

REST-assuredは、起動しているRESTサービスに対して、独自のDSLによりRESTクライアントとしてアクセスし、レスポンスのステータスコード、ヘッダ、コンテンツなどを検証します。主な機能は次のとおりです。

  • 独自DSL
  • JsonPathやXPathによるレスポンスの検証
  • BASIC認証、Digest認証、Form認証、OAuth1/2など様々な認証をサポート
  • SSLサポート
  • オブジェクトとJSON/XMLのシリアライズ、デシリアライズ
  • リクエスト、レスポンスのダンプ

ライブラリ

次のライブラリが必要です。

    <dependency>
        <groupId>com.jayway.restassured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>2.5.0</version>
    </dependency>
    <!-- JsonPathを使用する場合 -->
    <dependency>
        <groupId>com.jayway.restassured</groupId>
        <artifactId>json-path</artifactId>
        <version>2.5.0</version>
    </dependency>
    <!-- XPathを使用する場合 -->
    <dependency>
        <groupId>com.jayway.restassured</groupId>
        <artifactId>xml-path</artifactId>
        <version>2.5.0</version>
    </dependency>

テスト対象の起動

テストをするには、テスト対象のRESTサービスを起動する必要があります。別プロセスとして、RESTサービスを起動しておくか、Junitのテストごとに起動するようにします。 RESTサービスが、Springを使用している場合は、次のようにアノテーションをテストクラスに付与すると、テストごとにRESTサービスが起動します。

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class RestServiceTest {
    :

テストの共通設定

RESTサービスのホストやポート番号など、各テストで共通的に使用する設定をまとめて設定することができます。@Beforeを付与したメソッドで共通的な設定を行うと、各テストがシンプルになります。

    /**
     * 各テストの前に起動するメソッド。
     */
    @Before
    public void setUp() throws Exception {
        // ポート番号
        RestAssured.port = 8080;
        // プロキシー
        RestAssured.proxy("localhost", 8888);
    }

詳細は、Default Valueを参照してください。

テストメソッドの実装例

テストメソッドには、REST-assuredが提供するDSLで、リクエストの設定、送信、レスポンスの検証を行います。

    @Test
    public void testSelectListJson() throws Exception {
        // リクエストの設定
        given().
            // リクエストヘッダなどをすべてログ出力
            log().all().
            // ヘッダ"Accept"を設定
            accept(ContentType.JSON).
        // リクエストの送信
        when().
            // GETメソッドで"/api/p/"に送信
            get("/api/p/").
        // レスポンスの検証    
        then().
            // レスポンスヘッダなどをすべてログ出力
            log().all().
            // ステータスコードがOK(200)であること
            statusCode(200).
            // ヘッダ"Content-Type"が正しいこと
            contentType(ContentType.JSON).
            // JSONに含まれるidが、1と2であること
            body("id", hasItems(1, 2)).
            // JSONに含まれるnameが、一致していること
            body("name", hasItems("Mantis Plugin", "GitBucket Plugin"));
    }

リクエストの設定

given().に続けて、ヘッダ、BODYとして送信するデータの設定などを行います。使用できるメソッドは、RequestSpecification を参照してください。主なメソッドを示します。

ログ

  • log リクエストのヘッダやBODYをログ出力します。
    given().
        // ヘッダとBODYをログ出力
        .log().all().

ヘッダ、クッキー

  • accept Accept ヘッダを設定します。
    given().
        accept(ContentType.JSON).
  • contentType Content-Type ヘッダを設定します。
    given().
        contentType("application/json").
  • header, headers 任意のヘッダを設定します。
    given.
        header("Content-Type", "application/json").
  • cookie, cookies クッキーを設定します。
    given.
        cookie("name", "value").

パラメータ

  • queryParam, queryParams, queryParameter, queryParameters クエリーパラメータを設定します。キーと値はエンコードされます。
    given().
        // "/api/p/?name=camel"にアクセス
        queryParam("name", "camel").
    when().get("/api/p/").
  • formParam. formParams, formParameter, formParameters フォームパラメータを設定します。
    given().
        contentType(ContentType.URLENC).
        formParam("name", "camel).
  • multiPart マルチパートを設定します。
  • pathParam, pathParams, pathparameter, pathParameters パスパラメータを設定します。
    given().
        accept("application/json").pathParam("id", 1).
    when().
        get("/api/p/{id}/").

認証

  • auth, authentication
    認証(basic, digest, form, oauth,oauth2など)を設定します。
    given().
        auth().
        preemptive().
        basic("user", "passwd").

コンテンツ

  • body オブジェクトを、指定した ObjectMapper に応じてJSONもしくはXMLに変換してBODYに設定します。POST、PUT、PATCHメソッドのみ対象です。 ObjectMapper が未指定の場合は、 Content-Type ヘッダをもとに変換します。
    given().
        contentType("application/json").
        // pluginはJSONに変換される
        body(plugin).

リクエストの送信

when(). に続けて、リクエストのメソッドの指定(get,post, put, delete, head, options, patch)およびURLを指定します。

レスポンスの検証

then(). の後に、レスポンスの検証を行います。使用できるメソッドは、ResponseSpecificationを参照してください。主なメソッドを示します。

ログ

  • log レスポンスのヘッダやBODYをログ出力します。
    then.
        // BODYをログ出力
        log().body().

ステータスコード

  • statuscode ステータスコードを検証します。
    then().
        // OKであること
        statusCode(200).

ヘッダ、クッキー

  • contentType ヘッダ Content-Type の値を検証します。
    then().
         // ヘッダ "Content-Type"が"application/json"であること
         contentType(ContentType.JSON).
  • header, headers 指定したヘッダの値を検証します。
    then().
        statusCode(201).
        // ヘッダ "Location"の値が正しいこと
        header("Location", "http://localhost:9999/api/p/3/").
  • cookie, cookies クッキーの値を検証します。
    then.
        cookie("name", "camel").

コンテンツ

  • body レスポンスに含まれるBODYの値を検証します。
JSONの場合

次のJSONをレスポンスとして返す場合を例に説明します。

[
    {
        "id": 1
        "name": "Mantis Plugin",
        "description": "バグ管理システムのMantisbtと連携します。",
    },
    {
        "id": 2
        "name": "GitBucket Plugin",
        "description": "GithubクローンのGitBucketと連携します。",
    }
]

JSONの値を指定するには、JSonPathを使用します。また、値の比較には、HamcrestのMatchers APIを使用します。

  • idの値が、1と2であること。
    then.
        // JSONに含まれるidが、1と2であること(順不同)。
        body("id", hasItems(1, 2)).

  • 2番目の nameの値が"GitBucket Plugin"であること。
    then.
        // リストの2番目(0始まり)の"name"が正しいこと
        body("[1].name", equalTo("GitBucket Plugin")).
  • Pluginの情報が2つであること。
    then().
        body("size()", equalTo(2)).

レスポンスのJSONが次の形式の場合、

    {
      "plugin":[
            {
                "id":1,
                "name":"Mantis Plugin",
                "description":"バグ管理システムのMantisbtと連携します。",
            },
            {
                "id":2,
                "name":"GitBucket Plugin",
                "description":"GithubクローンのGitBucketと連携します。",
            }
        ]
    }

JSONの指定方法が上記とは異なることに注意してください。

    then.
        // JSONに含まれるidが、1と2であること(順不同)。
        body("plugin.id", hasItems(1, 2)).
        // リストの2番目(0始まり)の"name"が正しいこと
        body("plugin[1].name", equalTo("GitBucket Plugin")).
        // Pluginの情報が2つであること
        body("plugin.size()", equalTo(2)).
XMLの場合

次のXMLをレスポンスとして返す場合を例に説明します。

  <?xml version="1.0" encoding="UTF-8"?>
  <plugins>
    <plugin id="1">
      <name>Mantis Plugin</name>
      <description>バグ管理システムのMantisbtと連携します。</description>
    </plugin>
    <plugin id="2">
      <name>GitBucket Plugin</name>
      <description>GithubクローンのGitBucketと連携します。</description>
    </plugin>
  </plugins>

XMLの値を指定するにはXPathを使用し、値の比較には、HamcrestのMatchers APIを使用します。

  • 1番目の name の値が"Mantis Plugin"であること。
    then().
        body(hasXPath("/plugins/plugin[1]/name", is("Mantis Plugin"))).

または、次の書き方も可能です。

    then().
        body("plugins.plugin[0].name", is("Mantis Plugin")).
  • 2番目の id の値が"2"であること。
     then().
         // idは属性のため"@"を使用する
         body(hasXPath("/plugins/plugin[2]/@id", equalTo("2"))).

または、

    then().
        body("plugins.plugin[1].@id", equalTo("2")).
  • Pluginの情報が2つであること。
    then().
        body("plugins.children().size()", equalTo(2)).

レスポンスの取得

レスポンスを直接取得したい場合は、extract().response() を使用します。

    // レスポンスの取得
    Response response = given().
                            accept(ContentType.JSON).
                        when().
                            get("/api/p/").
                        then().log().all().
                            statusCode(200).
                            contentType(ContentType.JSON).
                            body("id", hasItems(1, 2)).
                        extract().
                            response();
    // レスポンスから1番目のidの値を取得                        
    int id = response.path("[0].id");     
    // レスポンスからヘッダの値を取得
    String contentType = resaponse.header("Content-Type");
    // クッキーの値を取得
    String name = response.getCookie("name");
    // ステータスコードを取得
    int code = response.getStatusCode();

レスポンス全部ではなく、特定の値を取得した場合は、extract().path() を使用します。

    // 1番目のidの値を取得
    int id = given().
                accept(ContentType.JSON).
                when().
                    get("/api/p/").
                then().log().all().
                    statusCode(200).
                    contentType(ContentType.JSON).
                extract().
                    path("[0].id");