天然パーマです。

なんだかんだ Test::JSON::RPC::Autodoc をつくった

Autodoc について

RESTfulもしくはそれに準ずるWeb APIやJSON-RPCなどのバックエンドとモバイルアプリ間の通信を実装する案件はおそらく昨今非常に多いかと思われます。弊社もJSON-RPCを裏方に持ち、iOSとAndroidアプリ、もしくはフロントのWebがそれを呼び出す仕組みを持っています。その際にWeb APIやRPCを呼び出すための「ルール」をどのようにお互いの技術者が共有するか?は僕らの課題でもありました。そのルールとして

  • リクエストパラメータ
  • パラーメータごとの型やお約束
  • リクエストのサンプル
  • レスポンスの値
  • サンプルリクエストに対するレスポンス

などがドキュメント化されていると便利でしょう。それは「手で仕様書を書く」という一点張りで解決出来ることではありますが、日々追加、修正されるAPIに対しては効率が悪いと感じてしまいます。そこで、もう去年の話ですが「 Autodoc 」というライブラリしいては「概念」を見つけました。

元々Autodocは r7kamura 氏が作ったRubyのライブラリです。

Rails/Rackのアプリケーションに対して、リクエストを送るテストを書くと、上記で上げたようなリクエストパラメータやレスポンスの情報がMarkdown形式でドキュメントとして保存されます。それを例えば、GitHubのレポジトリなどにpushすれば自動的にMarkdownがレンダリングされるので、Web APIのアプリケーションでは呼び出しとそれに対するレスポンスが一目瞭然というわけです。

簡単に言えば

テストを回すだけでWeb APIとかのドキュメントを履いてくれる

ってことで、こんな効果があります。

  • 仕様書を書く必要が無い
  • MarkdownのURLなど「ありか」を教えるだけで仕様を共有できる
  • 自然と結合的なテストを書くようになる

このライブラリを受けて、 moznion 氏がPerlに移植したのが Test::JsonAPI::Autodoc となります。

しっかりと JSON-RPCにも対応している旨がPODに書いてある のではありますが、僕は僕で自らのプロダクトで使うために「Shodo」というライブラリをつくりました。

Shodo ディストリビューションに含まれる「Test::Shodo::JSONRPC」は去年から弊社のプロダクトで使われ続けていて、 Autodoc の利点である、仕様の共有を楽にするってことと、テストを必然的に書くようになるという恩恵を受けることになりました。実際、iOS/Androidアプリの開発はパートナー会社のエンジニアさんとやっているのですが、GitHubのプライベートレポ内にあるドキュメントのURLを教えているため、仕様に関する議論は最低限で済みます。

Test::JSON::RPC::Autodoc

はい。以上、前置き終わりです。

主題は先ほど「 Test::JSON::RPC::Autodoc 」をリリースした件です。実は、Autodoc に対応する形で、この頃では JSON Schema と呼ばれるJSONに対するデータ定義を用いる手法も取られています。JSON Schema 自信を仕様とみなしドキュメントを生成したり、データのバリデーションルールを統一化する流れです。JSON Schema についても r7kamura 氏のBlog記事が分かりやすいですね。

で、まぁそんな時に「 何をいまさら 」って感じですが、

  • 今まで使ってきた Shodo について「キラキラネーム」を付ける必要が無く何をやるモジュールか分かりにくい
  • 僕が使うのは特に「JSON-RPCの Autodoc 機能」なのでそれに特化させれば良い
  • JSON Schema に関しては興味深いがスキーマ定義を管理するのが少々面倒でまた現在バリデーションルールを他の言語実装と共有必要は無い
  • 欲しいのはドキュメントでありルールをテストの中に書いてそれを自動生成するのは1年間使ってきて有益だと判断出来た
  • と「割り切る」
  • Shodo の中でも修正した点があった

以上の理由で Test::JSON::RPC::Autodoc をつくった形です。

使い方はJSON-RPCなPSGIアプリケーションに対して…

my $app = MyApp->new->to_psgi();
my $test = Test::JSON::RPC::Autodoc->new(
    document_root => './document',
    app => $app,
);

my $req = $test->new_request();
$req->params(
    language => { isa => 'Str', default => 'English', required => 1, documentation => 'Your language' },
    country => { isa => 'Str', documentation => 'Your country' }
);
$req->post_ok('echo', { language => 'Perl', country => 'Japan' });

my $res = $req->response();
is $res->code, 200;
my $data = $res->from_json();
is_deeply $data->{result}, { language => 'Perl', country => 'Japan' };

$test->write('sample.md');
done_testing();

このように使います。Test::JSON::RPC::Autodocインスタンスの new_request メソッドでつくったオブジェクトはHTTP::Requestを継承したものとなり、params メソッドでRPCをコールする際のパラメータを定義します。内部でData::Validatorを使っているのでそれに近い記法でルールを書きます。また、post_ok もしくは post_not_ok メソッドで実際のレスポンスが正しく返却されているかもテストします。

レスポンスのオブジェクトはHTTP::Responseのインスタンスですが、 from_json メソッドを追加してレスポンスボディを簡単にJSONからPerl-Objectへ変換出来るようにしました。最後は、Test::JSON::RPC::Autodocの write メソッドを使って予め指定していたドキュメントルートへMarkdownファイルを書き出すことが可能です。

そのMarkdownをGitHubで見ると以下のような見栄えになります。

RPC Document

先ほどCPANinzeしたばかりで、これから弊社プロダクトで使っていくところです。似たような要望がある方は少ないかもですが、よかったら使ってみてください!

PS.

全然関係ないけど今年も81忘年会やるからどんな世代の方もWelcome!