railsでRESTfulなAPIを書いていて、テストはrspecを使うと決めていたのだが、対象がRESTful APIということで、 何か気にすることはあるのだろうか。テストというか、設計というか、これまで実践していたことと、 今回調べて新たに発見したことを合わせて、纏めておく。
テスト対象
テスト対象は、rails4で作ったRESTfulなAPI。全てのAPIはJSONフォーマットのみ対応。 つまり、htmlとかxml形式の応答はしない。今回試したのはRails4。
rspecとその他のgem
テスト環境にいくつかgemを追加して、テストの準備をする。 追加するのは、
の各gem。それぞれのgemの用途と設定をちょっと書きます。rspec
テストを記述する。rspec、railsアプリのrspecでのテストの仕方は、
- スはスペックのス 【第 1 回】 RSpec の概要と、RSpec on Rails (モデル編)
- スはスペックのス 【第 2 回】 RSpec on Rails (コントローラとビュー編)
- 海外記事翻訳シリーズ 【第 1 回】 RSpec ベストプラクティス
railsアプリのテストでrspecを利用するための手順
-T付きでrails newする。
デフォルトのrailsアプリのテストは、Test::Unitが使われ、test用のディレクトリが生成される。-TオプションをつけるとTest::Unitが無効になり、testというディレクトリが生成されない(rspecのテストコードは、specディレクトリに入れる。)
$ rails new application_name -T
Gemfileの記述
Gemfileに以下を追加する。
group :development, :test do gem 'rspec-rails' end
bundle installの実行
bundlerを使ってrpsecをインストールする。
$ bundle install
rspecの初期設定
rspecをrailsアプリに組み込むと、railsコマンドのgeneratorにrspec:installが追加される。 追加されているかは、rails g -hを実行すれば確認できる。
$ rails g -h (略) Rspec: rspec:controller rspec:helper rspec:install rspec:integration rspec:mailer rspec:model rspec:observer rspec:scaffold rspec:view (略)rspec:installを実行して、specディレクトリを作るとともに、spec_helper.rbを生成する。
$ rails g rspec:installここまでが、rails上でrspecを使うためのベーシックな手順。
simplecov
simplecovは、ruby用のコードカバレッジ計測ツールで、railsでも使える。 rspecとも簡単に連動できるし、生成されるレポートも見やすい。
Gemfileの記述
Gemfileに以下を追加する。
group :development, :test do gem 'simplecov' end
bundle installの実行
bundlerを使ってsimplecovをインストールする。
$ bundle install
rspecとsimplecovの連携
spec/spec_helper.rbの先頭に次のコードを追加するだけで、rspecが動くたびにカバレッジを計測してくるようになる。 計測結果は、デフォルトではcoverageディレクトリにhtmlファイルとして出力される。
require 'simplecov' SimpleCov.start 'rails'
guard-rspec
rspecを使ってテストを書いている場合、コードを直してspecを直して、rake specで全てのテストが問題なく通ることを確認して、というサイクルで開発していくことになる(このサイクル自体はrspecを使う場合に限らないけど。)。この時に修正したモジュールに対するspecの実行とか、全てのspecの実行とか、いちいちコマンドを叩いているのが面倒になる。そんなとき、guardを使うとspecの実行を自動化できるようになる。guardは、プロジェクト内のファイルの変更を監視して、変更が発生すると、ある動作を実行する、というもの。なので、rubyファイルやspecファイルをguardに監視させて、変更が発生したらテストを実行する、というように使う。
に詳しい説明があってとても参考になる。
Gemfileの記述
Gemfileに以下を追加する。
group :development, :test do gem 'guard-rspec' end
bundle installの実行
bundlerを使ってgurad-rspecをインストールする。
$ bundle install
guardの設定
guardは、Guardfileの記述に従って動作する。Guardfileは、
$ bundle exec guard init rspecで生成する。Guardfileに監視対象と、実行するspecの組み合わせを追加していくのだけど、これについては後述する。
guardの実行
guardを実行するには、
$ bundle exec guardとする。すると、
13:36:21 - INFO - Guard is using NotifySend to send notifications. 13:36:21 - INFO - Guard is using TerminalTitle to send notifications. 13:36:21 - INFO - Guard::RSpec is running 13:36:21 - INFO - Guard is now watching at '監視対象パス' [1] guard(main)>と表示され、監視が始まる。guardのプロンプトで、"all"(またはEnter)と打つと、全てのspecが実行される。
spring
guardを使ってrspecを動かす手間がかなり省けるようになると、次に気になるのがspecの実行時間。specの量が増えてくると、実行時間が長くなるし、そもそも起動時間も長くなって時間が勿体無く感じてくる。 springを使うと、railsの環境を先読みしてくれるようになるので、様々なプロセスの起動が速くなる。これをguardと組み合わせて使うことで、guardが実行するrspecを高速化することができる。
Gemfileの記述
Gemfileに以下を追加する。
group :development, :test do gem 'spring' end
bundle installの実行
bundlerを使ってspringをインストールする。
$ bundle install
guardとspringの連携
guardとspringを合わせて使うには、Guardfileをちょっと修正するだけでよい。
guard :rspec, spring: true do # spring: true を追加する。 :(略) end
factory_girl
railsでrspecを使うときのテストデータに関して面倒なことがある。デフォルトだと、spec/fixturesディレクトリにテーブル(model)単位にymlファイルを作ってテスト時にロードして、っていうやり方になるけど、これだとモデル間のリレーションを考慮したテストデータづくりが非常に面倒くさいし、データが増えると管理できなくなる。この問題は、facotyr_girlを使うことで対応できる。factory_girlは、fixturesに定義していたymlをrubyで記述できるDSLを提供してくれている。そのため、データの定義やデータ間の関連がスッキリ書けるようになる。
- strix01: railsでテストデータの管理にfactory_girlを使うのが良さそう。
- [rails]has_manyなフィクスチャを書くのに疲れたらFactory Girlがオススメ! - func09
- factory_girl で最低限知っておきたい4つの使い方 - (゚∀゚)o彡 sasata299's blog
Gemfileの記述
Gemfileに以下を追加する。
group :development, :test do gem 'factory_girl_rails' end
bundle installの実行
bundlerを使ってfactory_girlをインストールする。
$ bundle install
JSONを返すRESTなAPI
ここまではどちらかというと、あまりJSONとかRESTとかとは直接関係しない、rspecを使ったテストを効率良く進めるための話題。 ここからはJSONを返すRESTなAPIのテストに関する話題。
にもろに影響を受けている。
APIのバージョニングを考慮する。
実際に経験したことでは、スマートフォンアプリをクライアントとするAPIを作っていて、アプリの機能を追加・変更して行く過程で、 API自体にバージョンの概念が必要になって、APIのリソースのURLにバージョン番号を埋めた、ってことがある。具体的には、例えばユーザ情報を取得するAPIが、
https://api.exapmle.com/users/id.json?id=xxxxのように定義していて、ある時点から応答するデータの内容が大幅に変える必要がある、という場合。やったのは、
https://api.exapmle.com/v2/users/id.json?id=xxxxという新しいURLを定義して、元々のAPIと並行して運用するということをした。このやり方(URLにバージョンを埋める)は、Twitter APIやFoursquare APIでも同じようなやり方をしているので、結構メジャーなソリューションなのかと思っている。railsでの実現方法は、
が参考になる。
APIのフォーマットをJSONに限定する。
railsのコントローラをgenerateコマンドで定義すると、デフォルトではhtmlを応答するアクションが生成されるけど、APIを作っている場合、htmlを応答することはまず無いし、一度JSONを返すと決めたら、後々xmlだとか他のフォーマットに対応させる、という変更が発生することも少ないのではないか、と思っている。なので、始めからJSONのみに限定したAPIにしてしまえば、設計もテストも楽になると考えた。
railsであるURLがデフォルトでJSONのみに対応するようにするには、
- routes.rb
- controller
- view
config/routes.rb
例えば、ユーザ情報に関するAPIをrailsのresourcesを使いつつ、JSONに限定する場合は、
namespace :v1, defaults: { format: 'json' } do resources :users endとしてあげると、通常必要となるURLの最後の".json"が不要となる。
controller
コントローラ側でJSONのみに対応させるには、
module V1 class UsersController < ApplicationController respond_to :json def show @user = User.find(id: params[:id]) end end endのように、先頭でrespond_toを読んであげれば個々のアクションでrespond_toを書く必要がなくなる。
view
JSON形式の応答も、独立したviewを作成することができる(知らなかった・・)。rails3.2から導入されたjbuilderというgemを使って、例えば上述のusers#showに対するviewは、app/views/v1/users/show.json.jbuilder というファイルを作ればよい。
jbuilderに関する解説もRailsCastの、
がわかりやすい。簡単にJSONのviewが作れるので感動的。
ここまでは、JSONを返すAPIの設計の話だったけど、次はテストの話。
テストは、Request Specに書く。
前述の「Rails API Testing Best Practices With RSpec」によると、APIのテストは、(Controller Specではなく、)Request Specに書くべき、ということらしい。 APIの呼び出しと応答(JSON)をテストする場合は、コントローラ単体というよりは、ルーティング、コントローラ、モデルなど各スタック間のIntegration Testに近いイメージで、rspecのRequest Specは、それがテストできるように設計されているから、ということっぽい。
JSON Helperをこしらえる。
JSONを返すアクションのテストを書くと、確かに
JSON.parse(response.body)が頻発する。これをテストごとにいちいち書かずに、モジュールを作ってDRYに行こうぜ、と。
# spec/support/request_helpers.rb module Requests module JsonHelpers def json @json ||= JSON.parse(response.body) end end endというモジュールを作っておいて、spec_helperで、
RSpec.configure do |config| config.include Requests::JsonHelpers, type: :request endとしておけば、簡単にパースされたJSONオブジェクトにアクセスできるようになる。
まとめ
railsでREST Web APIをつくってrspecでテストする、という場合に、テスト関連ライブラリの準備や、APIの設計方法、テストのポイントをグワーっと書いてみました。中身が薄かったり、言葉足らずだったり、まとめきれてない感じもあります。更に精進して、改善していきたいと思います。
0 件のコメント:
コメントを投稿