読者です 読者をやめる 読者になる 読者になる

HTTPアクセスを記録/再生してテスト時に使える、Betamaxを試してみたよ

Groovy

「初夢が別の人と結婚する夢だった」と妻に話したら、「私も別の人と結婚してる夢を見て、子供も産まれてた」と返されて負けた気がしたikikkoです。あけましておめでとうございます。今年もよろしくお願いします。

http://livedoor.blogimg.jp/pistolskate-moso/imgs/f/e/fe3c0a58.jpg


概要

Betamax - Record / playback testing proxyとは・・・うまく説明できる気がしないので、丸ごと引用してきます。

BetamaxはWebへのアクセスを記録して再生することのできるrecord/playback proxy です。

最 近 の ア プ リ ケ ー シ ョ ン は TwitterFacebook な ど 外 部 のWebAPIと連携するものが多くなってきていますが、Betamaxを利用すると実際にWebAPI やWebサイトへのアクセスを行わずにアプリケーションのテストを行うことができます。

Betamax はHTTP リクエスト・レスポンスのペアを、HTTP リクエストの内容をキーとして「tape」というテキストファイル(YAML) に記録します。テストケースに対してtapeを指定してやることで、既に記録済みのHTTP リクエストに対してはtape の内容が再生されます。未記録のHTTPリクエストであれば、実際に得られたレスポンスをキャプチャしてtape に記録します。

http://grails.jp/g_mag_jp/file/gmagazine_4.pdf

Groovyカテゴリに入れてますが、JUnitをはじめとしたJavaオンリーでも多分使うことができます(未検証)。

もうちょっとちゃんとした説明は、下記を参考にしてください(説明されている内容はどちらも大体同じです)。

メリット

いくつか参考資料中でも述べられていますが、外部にアクセスにいかないので下記のようなメリットがあります。

  • オフラインでテスト実行できる
  • スローテスト問題を解決できる
  • (ネットワークや接続先での障害といった)外部環境の影響を受けにくい
  • 更新系APIでの副作用問題が起きない

また、通信データを加工できるので

  • レアケースを簡単に再現できる
  • (認証情報などの)共有するべきでない個人情報を除去した上で、データを共有できる

というのもあげられるでしょう。

外部APIを使ったテストには、ダミーのモックを使うという選択肢もあります。ただ、Betamaxでは実際の通信データを再利用するので、まるごとモックを用意するよりは信頼できるデータといえるでしょう。

サンプル

サンプルでは、Cacoo APIを使っています。具体的には、図の新規作成/コメントの追加をCacoo API経由で行い、そのレスポンスから最新のコメントを取得するものです。Cacoo APIを含んだコードでも、毎回外部(Cacoo)に接続しに行かずにテストします*1

サンプルコードは、Githubの下記の場所に置いています。Gradleでプロジェクトを作成しておりGradle Wrapperも用意しているので、cloneして"./gradlew test"と入力すればサンプルを実行することができます(Groovy環境も必要ありません)。テスト実行後、"build/reports/tests/index.html"で実行結果や出力を確認できます。

解説

メインのコードは以下です。

Betamaxの設定は大きくわけて、下記の2点です。

  • @RuleにRecorderを設定する
  • 各テストケースに@Betamaxを指定する

まず、RuleにRecorderを設定します。これは、単純に宣言するだけで十分です。

// Betamaxを使用して、通信内容をtapeに記録/読込できるようにする
@Rule Recorder recorder = new Recorder()

@Betamaxでは、tapeの保存先を指定します。また、tapeから読み込むときの判定条件を「リクエストの(ホスト名+パス)が一致したとき」というように変更しています。デフォルトだとURI一致なのですが、今回はAPI Keyをクエリーストリングに含むのでURIが変わる可能性があったので、このようにしています。

// 保存先のtape名を指定, ホスト名/パスが一致した場合はtapeから読み込む(クエリは判定対象外)
@Betamax(tape="create diagram", match=[
	MatchRule.host,
	MatchRule.path
])
def "図の新規作成"() {
  ...
}
tapeの記録

通信内容をtapeに記録するために、初回はHTTP通信が発生します。記録された通信内容は、YAML形式で保存されます。以下のファイルが、tape内容です。

Cacoo APIを使う場合はAPI Keyが必要ですし、記録されたtapeにもAPI Keyも合わせて記録されています。ですが、push前に上記のtapeを直接編集して、API Keyの情報を除去しました。このように、一度tapeを作成してから、都合がいいようにデータを改ざんできるのもメリットの一つです。

tapeの再生

2回目以降は、作成されたtapeを元に通信が再生されます。以降はネットワークにつながっている必要がありません。また、API Keyを設定しておく必要もありません。

pushをしたものはすでにtapeの記録を行った状態なので、再生から実行することができます。

問題点・注意事項

HTTPSをサポートしていない

一番大きいのはHTTPSをまだサポートしていないということ。Issueとしてあがっているので認識はしていると思うのですが、いつサポートされるのかは分かりません。

これサポートされないと、多分厳しいよな・・・

HttpClientを使う場合は、Proxyの設定が必要

Betamaxでは、実行時にJavaのProxy設定をいじってBetamaxが起動しているサーバ(デフォルトではlocalhost, 5555)に向けます。ただ、HttpClientのデフォルト設定では、JavaのProxyの設定を参照しないようです。HttpClient(や内部でBetamaxを使っているHttpBuilder)でもBetamaxを使うために、ちょっと設定を追加する必要があります。サンプルコードではHttpBuilderを使っているので、それ用の設定も追加しています。

詳しくは、下記を参照してください。

公式サイトのドキュメントとリリース版のモジュールが一致していない

これにしばらくハマりました。ドイヒーですよね。

公式サイトにまだリリースされていない機能も書かれています。具体的には、betamax.propertiesというプロパティファイルでいくつか設定を変更できるのですが、これのignoreHosts/ignoreLocalhostが現在リリースバージョンの1.0には含まれていません。

他にもあるかもしれないので、注意しましょう。


どうでもいいですが、テニスの王子様って面白いですよね (・∀・)

*1:Backlogの方がAPI連携ネタとして実用的なことが多いので普段はBacklogを使ったサンプルが多いのですが、ちょいちょい問題があったので今回はCacoo APIを例にあげました。問題については後述します。