RailsのRESTful APIをテストで理解する

http://www.commandercoriander.net/blog/2014/01/04/test-driving-a-json-api-in-rails

1 comment | 0 points | by WazanovaNews 3年以上前


Jshiike 3年以上前 | ▲upvoteする | link

Pivotal LabsのEno ComptonがRailsでJSON APIをテストを通して理解できるようにまとめてます。「Railsアプリをてがけると、いずれ、シングルページアプリ、モバイルクライアントのためにRESTful APIが必要になるだろうから練習用に。」ということで、コードはGithubで公開されています。

GET    /movies
POST   /movies
GET    /movies/:id
PUT    /movies/:id
DELETE /movies/:id

上記のrouteをサポートするために、Railsアプリ + RSpec + FactoryGirlを用意したら、まずは、GET /moviesのテストを書く。

# spec/requests/movies_spec.rb
describe "Movies API" do
  describe "GET /movies" do
    it "returns all the movies" do
      FactoryGirl.create :movie, name: "The Lord of the Rings"
      FactoryGirl.create :movie, name: "The Two Towers"    
      get "/movies", {}, { "HTTP_ACCEPT" => "application/json" }
      expect(response.status).to eq 200
      body = JSON.parse(response.body)
      movie_titles = body.map { |m| m["title"] }
      expect(movie_titles).to match_array(["The Lord of the Rings", "The Two Towers"])
    end
  end
end

APIにGETリクエストをシミュレーションするところは、

get "/movies", {}, { "HTTP_ACCEPT" => "application/json" }

xhrを使うこともできたが、

xhr :get "/movies"

練習のために、getで意図を明示的に表現するようにした。getは、path, HTTPパラメータ、その他のヘッダーを引数としてとれるが、ここではHTTP_ACCEPTヘッダーをリクエストして、サーバからJSONを返してほしい旨を指定している。

また、リクエストの後にセットされるresponse変数は、そこでJSONを使ってresponseのbodyを解析し、そこからbodyに返ってくるもの、例えば、movieのタイトルなど、にアクセスできるようになる。

このテストをパスさせるために、FactoryGirlでmovie factoryを定義する。それから、マイグレーションしてmovieテーブルをつくりためのmovieモデルを用意する。今のところは、title属性だけだが、興味がある場合は、詳細コードを確認されたし。

movie factoryとmovieモデルができると、次にrouteが必要になる。

# config/routes.rb
JsonRails::Application.routes.draw do
  resources :movies, only: [:index]
end

routeが設定されると、次はcontrollerがないことのエラーがでる。更に、indexアクションがないエラーに対応して、このようにcontrollerをセットする。

# app/controllers/movies_controller.rb
class MoviesController < ApplicationController
  def index
  end
end

Rails側は、HTMLページをレンダリングしたいのだろうと考えるので、テンプレがないというエラーをだす。そこで、RailsにJASONリクエストだけに応え、全てのmovieのJSONを返すように指示する。

class MoviesController < ApplicationController
  respond_to :json
  def index
    respond_with Movies.all
  end
end

これで初めてテストにパスする。その他のrouteも比較的同様に対応できる。次のエンドポイントは、/movies/:id

describe "Movies API" do
  # ...
  describe "GET /movies/:id" do
   it "returns a requested movie" do
      m = FactoryGirl.create :movie, title: "2001: A Space Odyssey"
      get "/movies/#{m.id}", {}, { "HTTP_ACCEPT" => "application/json" }
      expect(response.status).to eq 200
      body = JSON.parse(response.body)
      expect(body["title"]).to eq "2001: A Space Odyssey"
    end
  end
end

routeのリストにshowを追加し、下記のようにcontrollerをセットする。

# app/controllers/movies_controller.rb
class MoviesController < ApplicationController
  # ...
  def show
    respond_with Movie.find(params[:id])
  end
end

今のところ、APIへのリクエストをシミュレーションするときは、HTTP_AACEPTを指定するだけでよく、それをしなければ、リクエストとともに渡されるパラメータは無視されるだけである。しかし、movieのリソースを用意するときは、movieテーブルにあるものは、どんな情報でも渡す必要がある。

describe "Movies API" do
  # ...
  describe "POST /movies"
    it "creates a movie" do
      movie_params = {
        "movie" => {
          "title" => "Indiana Jones and the Temple of Doom"
        }
      }.to_json
      request_headers = {
        "HTTP_ACCEPT" => "application/json",
        "CONTENT_TYPE" => "application/json"
      }
      post "/movies", movie_params, request_headers
      expect(response.status).to eq 201 # created
      expect(Movie.first.title).to eq "Indiana Jones and the Temple of Doom"
    end
 end
end

POSTリクエストとともに渡すmovie_paramsの他にも、CONTENT_TYPEヘッダーを指定して、サーバにリクエストのボディのMIMEタイプを教えなくてはいけない。DELETEとPUTは、GETやPOSTと似ているので、ここでは取り上げない。興味があれば、ソースを確認ください。テスト駆動型のスタイルでRESTful JASON APIをつくるシンプルな練習だが、必須条項やHTTPリクエストのサイクルの肝を確認できると思う。自分にとってもこのインターフェースで、cURLを試すことができる。


[2013] ワザノバTop100アクセスランキング


#rails #api #テスト #シングルページアプリケーション #コーディング

Back