こんにちは、Web系エンジニアのナカガワです。
皆さん、REST APIのテストはどのようなツールを使っていますか?
私はJUnitでテストが書ける「Restfuse」を使っています。
今回、実プロジェクトでRestfuse + Jenkinsで定期的にREST APIをテストする仕組みを構築したため、このあたりのノウハウをまとめて書きたいと思います。
REST APIテスト自動化のゴール
ゴールは以下の二つです。
(1) APサーバ上で動作しているWebアプリケーションに対し、自動でREST APIテストを実施する。
(2) Jenkinsを用いてCIを実施可能にする。
まず今回は、前者のREST APIテストを実施するところまで紹介します。
Restfuseを使って、REST APIをJUnit上でテスト可能に!
先にも書きましたが、私が使ったのはRestfuseというツールです。
Restfuseは、HTTP/REST通信のテストを容易に実行できるようにするためのJUnitExtentionです。
ここが公式サイトです。
http://developer.eclipsesource.com/restfuse
なおREST APIのテストとして便利そうなフレームワークとしては、他にREST-assuredなどがあります。
今回は検証を自分でカスタマイズして書きたかったので、電文送信処理をアノテーション記述のみで実現でき、テストコードには検証コードを書くだけに絞れるRestfuseを選択しました。
ちなみに今回は扱わないのですが、Restfuseでは定期ポーリングテストやコールバック処理の記載など、REST APIを使う際にテストが必要なさまざまなパターンに対応しています。
Restfuseを使うための準備
1. 統合テスト自動化用のEclipseプロジェクトを作成。
統合テスト用にMavenを使って新規にEclipseプロジェクトを作りました。
統合テスト用に新規にEclipseプロジェクトは作るか、アプリケーションを実装したEclipseプロジェクトにテストを追加するかは好みが分かれると思います。
私の場合は単体試験相当のJUnitと統合試験相当のJUnitコードが混ざると見にくいと考え、別プロジェクトにしています。
※Eclipse上でMavenを用いてプロジェクトを作成する方法は以下のサイトが詳しいです。
archetypeはmaven-archetype-quickstartを選択して作成しました。
http://d.hatena.ne.jp/kagamihoge/20121228/1356695798
2. Restfuseを使うために必要なMavenのdependencies設定を追加。
以下に定義を記載します。
<!-- 依存関係 --> <dependencies> <!-- JUnit Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> <!-- HTTP Test --> <dependency> <groupId>com.restfuse</groupId> <artifactId>com.eclipsesource.restfuse</artifactId> <version>1.2.0</version> </dependency> </dependencies>
これで試験を実施する準備が完了しました。
REST APIテスト(テスト対象API説明)
今回は以下のAPIのテストを実施します。
1. リクエスト
HTTPヘッダ | 設定値 |
---|---|
URL | http://localhost:8080/restsample/getJson |
METHOD | POST |
ContentType | application/json |
HTTPボディ
{ "id" : "String値", "number" : "String値", "name" : "String値", "message" : "String値" }
2. レスポンス
HTTPヘッダ | 期待値 |
---|---|
ContentType | application/json |
期待するHTTPボディ
{ "result" : "success", "message" : "" }
REST APIテスト(テスト作成)
以下に作成したテストコードを掲載します。
RestSampleTest.java
import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import org.junit.Rule; import org.junit.runner.RunWith; import com.eclipsesource.restfuse.Destination; import com.eclipsesource.restfuse.HttpJUnitRunner; import com.eclipsesource.restfuse.MediaType; import com.eclipsesource.restfuse.Method; import com.eclipsesource.restfuse.Response; import com.eclipsesource.restfuse.annotation.Context; import com.eclipsesource.restfuse.annotation.HttpTest; /** * @author nakagawa * */ @RunWith(HttpJUnitRunner.class) public class RestSampleTest { /** * REST APIの設定としてURLを記載します。 */ @Rule public Destination restfuse = new Destination(this, "http://localhost:8080/restsample"); /** * レスポンスオブジェクトが格納されるフィールドを用意します。 */ @Context private Response response; /** * JSON取得APIに接続する。 * HttpTestにテストを実施するメソッドの設定を行います。以下の通り * 1. method : リクエストのメソッドを設定 * 2. path : リクエスト送信先のパスを設定。今回であれば、http://localhost:8080/restsample/getJsonにアクセスする。 * 3. type : リクエストのContent-Typeを設定する。 * 4. file : リクエストのbodyに設定する内容を記載する。 * * @throws Exception * エラー */ @HttpTest(method = Method.POST, path = "/getJson", type = MediaType.APPLICATION_JSON, file = "sampleData.txt") public void testGetJson_Status200() throws Exception { assertThat(response.getStatus(), is(200)); } }
sampleData.txtとexpectedValue.txtはそれぞれ以下の値としました。
sampleData.txt
{ "id" : "12345", "number" : "1", "name" : "nakagawa", "message" : "first step of automation test" }
expectedValue.txt
{ "result" : "success", "message" : "" }
以下ポイントを解説します。
1. Runnerの設定
RestfuseはJUnitExtentionです。
@RunWith(HttpJUnitRunner.class)をクラスの先頭につけることでRestfuseが動きます。
2. RootURLの設定
テスト対象のルートURLを設定したDestinationを生成します。
@RuleをつけることでRestfuseが設定を読み込み、URLを解決してくれます。
/** * REST APIの設定としてURLを記載します。 */ @Rule public Destination restfuse = new Destination(this, "http://localhost:8080/restsample");
3. レスポンスオブジェクトの格納先の追加
以下のように@Contextを付けたフィールドを用意すると
Restfuseが通信後にレスポンスを自動で格納してくれます。
/** * レスポンスオブジェクトが格納されるフィールドを用意します。 */ @Context private Response response;
4. テスト条件の指定
テスト条件を指定します。
指定方法はテストメソッドにHttpTestアノテーションを付けます。
それぞれ以下の値を設定します。
フィールド名 | 設定内容 |
---|---|
method | リクエストのメソッドを指定。 |
path | RootURLから先のパスを指定します。以下の記述だと、http://localhost:8080/restsample/getJsonにつながります。 |
type | Content-typeを指定します。 |
file | リクエストのbody要素に値を入れる場合には、ファイル名を記載することで対象のファイルを読みbodyに設定してくれます。 |
@HttpTest(method = Method.POST, path = "/getJson", type = MediaType.APPLICATION_JSON, file = "sampleData.txt")
REST APIテストを実施する。
Jenkinsで試験をする前に、まずは自分でWebアプリケーションを動かして、テストが実施できているか確認しておきます。
※テスト対象のWebアプリケーション(APサーバ)は事前に起動しておく必要があります
テストが実施されました。
無事レスポンスも返りチェックできています。
Restfuseを使うことで、通信処理はすべてFrameworkでやってもらい、検証処理の記載に絞ることができました。
通信を伴うテストだと、電文を送信するまでの事前条件だけでテストコードの半分が埋まってしまうことが多々ありました。
Restfuseによって、テストコードが検証処理だけになるのは見通しがよくなるのはうれしいですね。
Restfuseで通信を行う前の初期化をどうするか
ところで、Restfuseを使っていると、「事前のデータの初期化とかはどうやっているの?」という質問を受けることがよくあります。
Restfuseはアノテーションベースでテストを行うため、テストケース内で事前に初期化を行うためのコードを書く事ができません。
私はシンプルに、以下のようにしています。
1. BeforeClassアノテーションで初期化を行い、電文送信前にDBの初期化を実施。
2. AfterClassアノテーションで処理で、DBの値を削除。
この方法ではメソッドごとの柔軟な初期化こそできませんが、複雑になり過ぎることがありません。
より柔軟な初期化が必要な場合には、以下のような初期化を行ないます。
1. テストメソッド名と同名の初期化処理用ファイルを用意する。
2. @Ruleアノテーションを付けたTestNameをフィールドに設定する。
3. Beforeアノテーションで、TestNameを利用してテストメソッド名を取り出して、1を実行してDB初期化を行なう。
4. Afterアノテーションで、DBの値を削除する。
この方法では管理や見通しが少し複雑になるものの、柔軟な初期化ができるので、必要に応じて利用しています。
さて、次は今回作ったテストをJenkinsを用いてCI実施してみます。
ではでは!
Acroquest Technologyでは、キャリア採用を行っています。
- 日頃勉強している成果を、Hadoop、Storm、NoSQL、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
- 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
- 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
- OSSの開発に携わりたい。
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
キャリア採用ページ