2008-09-27

rest-client が便利

さてすでに書きましたが最近 API づいております。できるだけ RESTful な API を作りたいなぁと思っているわけですが、そこで問題になったのがテスト環境。

まず、ブラウザは使いものになりません。

  1. 通常の方法では GET, POST 以外のリクエストを投げられない
  2. 通常の方法では html, xml, plain text な response body 以外は表示できない

からです。

まぁ今回テストしたかったのは read only な API なのでリクエストメソッドとしては GET, POST だけでも問題はないのですが、response が application/json だったので

毎回毎回「ダウンロードダイアログが出る -> ダウンロード -> ファイルを開く」

作業が必要になり、さすがにこれはやっとれんと思いました。

そこでまず目をつけたのは以前インストールだけしてあった Firefox extension RESTTest. 結構初期のバージョンだったけど普通に使えます。ただ、フォーカスの当たっているデフォルトのボタンが [ OK ] になっていて、何も考えずにエンターを押すとウィンドウを閉じてしまうのがものすごくイライラ。要するに連続してテストしようにも毎回マウスでちくちく [ Send ] を押さないといけない。これはダサイ。

それでもなんとかサーバ側のコードを書く方が優先なのでちくちくやってました。とりあえず基本形ができたところでマシなテストツールを探します。

まずは RESTTest の最新バージョン。なんか addons.mozilla.org にログインが必要とかぬかし始めている。もうその段階で却下。できれば自動化したいので Ruby で書かれたものがいいなと思い、rubyforge を rest で検索して探します。で、なんとなく目をつけたのは以下。

それぞれ適当に試したのですが、

rest-client は irb を利用したテストが可能というところに強く惹かれました。

使い方は以下のような感じ。(今回試したのは 0.7)

$ restclient
irb> RestClient.get( 'URI' )

これで response body が画面上に現れます。例えば body が JSON の場合は{{fn "restclient 自身が rubygems でインストールされているのでいきなり require 'json' で ok です。"}}

require 'json'
$KCODE='u'

しておいて、

irb> JSON.parse( RestClient.get( 'URI' ) )

すると目視チェックが可能になります。さらに

require 'pp'

しておいて

irb> pp JSON.parse( RestClient.get( 'URI' ) )

すればインデントもバッチリ、とても確認しやすいです。これはブラウザをそのまま使っていたときよりも RESTTest を使っていたときよりもずっといい。

XML の場合は require 'hpricot' して

irb> doc = Hpricot( RestClient.get( 'URI' ) ); puts doc.search( '//item' )[0]

なんて風に使えます。

もちろん irb なので Readline が効いていれば履歴をたどって何回も同じテストを実行しやすいです。

ヘッダが見たければログを設定

rest-client はデフォルトではヘッダの情報は取れません。ヘッダの情報がほしい場合は

irb> RestClient.log=DEST

としてログを取得する設定をします。DEST は

  • ファイル名
  • stdout
  • stderr

が指定でき、stdout にした場合はそのまま irb 上に表示されます。

しかしここまでやっても実は取れる情報は

  • status code
  • Content-Type
  • Content-Length

くらいのもので、例えば Last-Modified などは取れません。つまり、現状では cache が絡むような凝った client は rest-client では書けないということです。

また status 200, 201, 202, 301, 302, 303 以外は例外が上がるので、例えば

RSpec などと組み合わせてテストコードを書こう

とする場合はその辺をうまく拾ってあげる必要があります。

認証の必要なリソースのテスト

RestClient::Resource を利用します。

irb> r = RestClient::Resource.new( uri, user, password )

でリソースを作って、

irb> r.get

やら

irb> r.post

なんて風に使います。

ヒストリで右往左往

上で irb なので履歴を利用して同じテストを何回でも試せると書いたばかりですが、実は irb は標準では history をログに落としてくれないので、起動するたびに履歴は忘れてしまいます。しばらくこのことを Twitter 上でブータレていたのですが、現在のところ以下の動作を確認しています。

  1. irb/ext/save-history を使う方法はリファレンスに 1.9 feature と書かれているが 1.8.4, 1.8.5 で問題なく動いている
  2. restclint 実行ファイルを呼び出した場合は 1 の方法も rlwrap1 をかます方法も使えない

ということで、結論としては

restclint 実行ファイルは利用せず、irb のプロンプトから require 'rest_client' してテストを行う

という形がベストのようです。いろいろ直接、間接に教えてくれた walf443 さん、eban さん、ありがとう!

なお、現在の自分の .irbrc は以下の通りです。

require 'irb/completion'
require 'pp'
require 'time'
require 'date'
require 'irb/ext/save-history'
IRB.conf[:SAVE_HISTORY] = 1000
$KCODE='u'

シンプルなもんです。これで require 'rest_client' を打てばすぐいけます。

まとめ

rest-client は API の開発に実に便利に使えそうです。今のところ自分の使っている範囲では cache 周りを除いては特に不満はありません。

またこれを利用すれば response body を Ruby の文字列として自由に加工可能なので、これまでブラウザ上で print デバッグしていたようなケースでも便利に使えることが分かってきました。例えば長過ぎる文字列をぶった切るとか HTML の一部を抜き出して確認するなんてことが比較的簡単に行えるので効率がいいのです。泥臭い print デバッグでも目印さえつけておけば rest-client + Hpricot 辺りでかなり楽できます。2そしてそれが Terminal だけで、つまりほぼキーボードだけでテスト可能なんてステキな話じゃないですか。

実は 「rest test」で探していたときは全然見つからなかったのですが、「rest client」で探すと Java のものとか Python のものとかいろいろ見つかりました。もしかするともっといいツールがあるのかもしれません。探すってやっぱ難しいですね。思いつかない言葉の組み合わせでしか見つからない情報は見つけられない -> 自分にとっては存在しないに等しい、って感じになっちゃう。

cf.

RestClient は 0.9 でヘッダが取れるようになってた

  1. rlwrap は readline が有効になっていないアプリで readline が使えるようになり、さらに history log も作ってくれる超便利なツールです。ただし irb で使うためには irb –noreadline と組み合わせないとたぶんおかしなことになるうえに、irb/completion が効きません。tab の動作が rlwrap 側に奪われてしまうからだと思います。 

  2. 本格的にブラウザを使ったテストの自動化には Selenium や WWW::Mechanize などを使う方がよりよいと思います。 

About

例によって個人のなんちゃらです