Ruby on Railsを使用して、テストコードを含めて簡単なAPIを作成してみようと思います。
私自身が初心者ですので、内容は非常に初心者向けになります。
基本はパーフェクト Ruby on Railsを見ながら実施し、補足などあれば作業メモとして残していきます。
今回作成するAPIについて
クライアントからリクエストされたIDでDBの値を返します。
そのため、適当なテーブルを作成します。
簡単なscaffold
を利用します。
$ rails g scaffold item name:string price:integer description:text
$ rails db:migrate
こんなテーブルが作成できました。
id | name | price | description | created_at | updated_at |
---|---|---|---|---|---|
integer | varchar | integer | text | datetime | datetime |
NOT NULL, PRIMARY KEY | NOT NULL | NOT NULL |
余談
SQLiteでSHOW COLUMNS
をやる
(PHP, MySQL, SQLite3) テーブルのフィールド名一覧を取得するには
PRAGMA table_info(items);
rails g scaffold
で特定のファイルを生成しない
今回は全て生成してしまいましたが、今後assets
などを生成したくないときは下記の設定を追記する必要があるようです。
Railsのgenerateコマンドを俺好みに設定する
+ config.generators do |g|
+ g.helper false
+ g.assets false
+ g.test_framework false
+ g.orm :active_record, migration: false
itemsテーブルのレコードを返すAPI
まず、コントローラーとビューを作成しました。
(erbファイルについては割愛します。)
class ApiController < ApplicationController
# GET /get_items
# GET /get_items.json
def get_items
@items = Item.all
respond_to do |format|
format.html
format.json { render json: @items }
end
end
end
+ get 'api/get_items', to: 'api#get_items'
これでapi/get_items.json
でアクセスすると、JSON形式で返してきます。
これはこれで良いかな、とも思いましたが、Ruby on RailsにはJbuilderというJSON生成のGemがあります。
こちらは初めからGemfile
に記述されています。
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
実はscaffold
で生成すると勝手に生成してくれてもいました。
jbuilderファイルを作成する意味としては、複数のモデルの値を返却したり、URLなどを返却するなど複雑な返却値のとき使用するようです。
今回は、scaffold
で生成されていたファイルを参考に、以下のように作成しました。
json.array! @items do |item|
json.extract! item, :id, :name, :price, :description, :created_at, :updated_at
json.url item_url(item, format: :json)
end
これで、items
テーブルの各カラムの値とURLが返されるようになりました。
(URLはお好みかと思います。)
このjbuilderファイルを作成しておくと、コントローラーの方でもjbuilderファイルを参照するため、下記のように変更できます。
- format.json { render json: @items }
+ format.json
これでも下記のようにブラウザで確認できると思います。
APIリクエスト時にパラメーターを渡される想定
例えば、items.idの2〜5まで欲しいなどのリクエストを想定し、下記のように修正します。
- @items = Item.all
+ before_id = params[:before_id]
+ after_id = params[:after_id]
+ @items = Item.where(id: before_id..after_id)
これで@items
に指定された範囲のレコードが入るので、JSON生成も絞られます。
ブラウザで確認もできますが、テストコードも書いてみたいと思います。
Rspecのインストール
Ruby on Railsには多くのテストフレームワークがあるようですが、今回はRspec
を利用します。
Rspec → http://rspec.info/about/
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platform: :mri
+ gem 'rspec-rails'
end
インストール後、Rspec
の設定ファイルを生成します。
$ bundle install
$ rails g rspec:install
create .rspec
create spec
create spec/spec_helper.rb
とりあえず設定ファイルは置いておき、モデル用のテストファイルを生成します。
$ rails g rspec:model item
create spec/models/item_spec.rb
簡単なテストコードを書いてみます。
Rspec
の書き方については、こちらの方が非常に分かりやすく記事にしてくださっております。
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」
require 'rails_helper'
RSpec.describe Item, type: :model do
describe '#name' do
context '空白のとき' do
let(:item) { Item.new(name: '') }
it 'validでないこと' do
item.valid?
expect(item.errors[:name]).to be_present
end
end
end
end
テストしてグリーンになればOKです。
$ bundle exec rspec spec/models/item_spec.rb
余談
Rspecをインストールした後にscaffoldを生成すると、specディレクトリ以下が生成される
Rails Q&A「Scaffoldで作成されるテストはそのまま使うべきか?」
FactoryGirl
テスト用にレコードを作成してくれるFactoryGirl
を使ってみます。
FactoryGirlのGithub → https://github.com/thoughtbot/factory_girl
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platform: :mri
gem 'rspec-rails'
+ gem 'factory_girl_rails'
end
FactoryGirl
用のファイルも生成します。
$ bundle install
$ rails g factory_girl:model item
create spec/factories/item.rb
FactoryGirl
の定義をします。
FactoryGirl.define do
factory :item do
sequence(:name) { |i| "商品名#{i}" }
sequence(:price) { |i| i }
sequence(:description) { |i| "説明#{i}" }
end
end
これで、FactoryGirl.create(:item)
でレコードをインサートしてくれます。
試しにコンソールで確認します。
$ rails c
Running via Spring preloader in process 50595
Loading development environment (Rails 5.0.0.1)
irb(main):001:0> FactoryGirl.create(:item)
(0.1ms) begin transaction
SQL (2.9ms) INSERT INTO "items" ("name", "price", "description", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["name", "商品名1"], ["price", 0], ["description", "説明1"], ["created_a016-10-02 05:32:52 UTC], ["updated_at", 2016-10-02 05:32:52 UTC]]
(3.6ms) commit transaction
=> #<Item id: 1, name: "商品名1", price: 0, description: "説明1", created_at: "2016-10-02 05:32:52", updated_at: "2016-10-02 05:32:52">
2回目は「商品名2」、3回目は「商品名3」。。。と入っていきます。
こちらをテストコード中で呼べば、オブジェクトを生成してくれますが、FactoryGirl
の設定ファイルに下記のように記述すれば、create(:item)
のみで呼べるようになります。
+ require 'factory_girl_rails'
RSpec.configure do |config|
+ config.include FactoryGirl::Syntax::Methods
# 以下、省略
このFactoryGirl
を利用して先ほどのテストコードに追記してみます。
今回はちゃんとしたレコードが入るので、「名前が空欄でないときはエラーが出ない」ことを確認します。
+ context '空白じゃないとき' do
+ let(:item) { create(:item) }
+
+ it 'validであること' do
+ item.valid?
+ expect(item.errors[:name]).to_not be_present
+ end
+ end
こちらでもオールグリーンになりました。
bundle exec rspec spec/models/item_spec.rb
API用のテストコードを書いてみる
下記のような形で、GETでパラメーターを渡し、取得したレコード数、before_id
で取得した値、after_id
で取得した値を比較しております。
今回はAPI用のテストコードになるため、spec/requests
ディレクトリに作成します。
FactoryGirl
のcreate_list
メソッドですが、これは第2引数で渡した数だけレコードを生成してくれます。
require 'rails_helper'
RSpec.describe "Items", type: :request do
describe "GET /api/get_items" do
let(:path) { '/api/get_items.json' }
context "GETパラメータ-が渡されているとき" do
before do
@items = FactoryGirl.create_list(:item, 10)
@params = { before_id: 2, after_id: 5 }
end
it "指定範囲のレコードが取得できる" do
get path, params: @params
expect(response.status).to eq 200
json = JSON.parse(response.body)
expect(json[0]['id']).to eq @items[1].id
expect(json[3]['id']).to eq @items[4].id
expect(json.size).to eq(4)
end
end
end
end
余談
デバッグするとき
いろいろ方法はあるかと思いますが、とりあえずテストコード中にputs
で埋め込んでおりました。
(PHPのときもvar_dump
やログ出力の簡単なものしか使っていなかったので。。。)
it "指定範囲のレコードが取得できる" do
get path, params: @params
expect(response.status).to eq 200
+ puts response.body
json = JSON.parse(response.body)
expect(json[0]['id']).to eq @items[1].id
expect(json[3]['id']).to eq @items[4].id
expect(json.size).to eq(4)
end
最後に
私がRuby on Railsの勉強を開始するときに参考にさせていただいたRUBY ON RAILS TUTORIALでも著者が書いておりましたが、「何かの機能を追加するとき、まずはテストコードから書く」という習慣を身に付けたいと思います。
簡単ではございますが、今回はこれで終了とさせていただきます。