RailsApp Rails RSpec 2018年 11月 26日
Rails 5 からは controller_spec を描くことは非推奨となり,request_spec で記述することが推奨されている(参照: Rails5でコントローラのテストをController specからRequest specに移行する ). request_spec では,controller_spec が行なっていたようなコントローラの内部実装に関わる点はテストから除外し,リクエスト/レスポンスにのみ関心を持つブラックボックステストを行う.
$ bin/rails g controller kurasus
Running via Spring preloader in process 63130
create app/controllers/kurasus_controller.rb
invoke haml
create app/views/kurasus
invoke rspec
invoke assets
invoke js
invoke css
# config/routes.rb
Rails.application.routes.draw do
devise_for :teachers
get 'pages/home'
root to: "pages#home"
resources :kurasus
end
$ bin/rails g integration_test kurasus
Running via Spring preloader in process 70314
invoke rspec
create spec/requests/kurasus_spec.rb
watch(rails.controllers) do |m|
[
rspec.spec.call("routing/#{m[1]}_routing"),
rspec.spec.call("controllers/#{m[1]}_controller"),
rspec.spec.call("acceptance/#{m[1]}"),
rspec.spec.call("requests/#{m[1]}") # これを追加
]
end
require 'rails_helper'
RSpec.describe "Kurasus", type: :request do
describe 'GET #index' do
it "works! (now write some real specs)" do
get kurasus_path
expect(response).to have_http_status(200)
end
end
end
# @param [Symbol] fg_key user の FactoryBot のキー
# @return [User] ログインしたユーザ
def login_teacher_as(fg_key)
@one = user_factory(fg_key)
sign_in @one
allow(controller).to receive(:current_teacher).and_return(@one)
@one
end
# @note サインアウト
def sign_out_one
sign_out @one
end
# @param [Symbol] fg_key user の FactoryBot のキー
# @note before と after を自動設置する.
def teacher_login(fg_key)
before { login_teacher_as fg_key }
after { sign_out_one }
end
ひとまず /kurasus のテストを書いてみる
require 'rails_helper'
RSpec.describe :Kurasus, type: :request do
# devise の integration test のためのヘルパを追加
include Devise::Test::IntegrationHelpers
# create, update, destroy 後にリダイレクトするパスを設定しておく(遅延実行なので必要となった時だけ作成される).
let(:return_path) { kurasus_path(edit_mode: true) }
# hkob でログインしている時の context
context 'login by hkob' do
# 事前に 2300 クラスを作っておく
let!(:object) { kurasu_factory :hkob2300 }
# 事前に 5300(obsolete) クラスを作っておく
let!(:others) { kurasu_factory :hkob5300o }
# テスト開始時に hkob でログイン,終了後ログアウトを設定
teacher_login :hkob
describe 'GET #index' do
# 通常時のコンテキスト (obsolete は表示しない)
context 'normal mode' do
# オプションなしで index を描画
subject { -> { get kurasus_path } }
# リクエストは正常
it_behaves_like :response_status_check, 200
# 2300 クラスは表示
it_behaves_like :response_body_includes, '2300'
# 5300 クラスは非表示
it_behaves_like :response_body_not_includes, '5300'
end
# obsolete 表示時のコンテキスト (全クラスを表示)
# obsolete オプションありで index を描画
context 'edit mode' do
subject { -> { get kurasus_path(edit_mode: true) } }
# リクエストは正常
it_behaves_like :response_status_check, 200
# 2300, 5300 クラスは共に表示
it_behaves_like :response_body_includes, %w[2300 5300]
end
end
end
# ログインしていない時の context
context 'not login' do
describe 'GET #index' do
subject { -> { get kurasus_path } }
# レスポンスはリダイレクト
it_behaves_like :response_status_check, 302
# 2300, 5300 クラスは共に非表示
it_behaves_like :response_body_not_includes, %w[2300 5300]
end
end
end
AbstractController::ActionNotFound:
The action 'index' could not be found for KurasusController
class KurasusController < ApplicationController
def index
end
end
touch app/views/kurasus/index.html.haml
Kurasus
login by hkob
GET #index
normal mode
behaves like response_status_check
response should be 200
behaves like response_body_includes
response body includes ["クラス一覧:", "小林弘幸", "2300"] (FAILED - 1)
behaves like response_body_not_includes
response body does not include 5300
edit mode
behaves like response_status_check
response should be 200
behaves like response_body_includes
response body includes ["クラス一覧:", "小林弘幸", "2300", "5300"] (FAILED - 2)
not login
GET #index
behaves like response_status_check
response should be 302 (FAILED - 3)
behaves like response_body_not_includes
response body does not include ["クラス一覧:", "小林弘幸", "2300", "5300"]
(中略)
Finished in 2.67 seconds (files took 0.21094 seconds to load)
7 examples, 3 failures
class KurasusController < ApplicationController
before_action :authenticate_teacher!
def index
@kurasus = current_teacher.kurasus.order_name
end
- content_for :title do
- @title = "#{t '.title'}: #{current_teacher.name}"
%table
- if @edit_mode
%caption= link_to t('kurasus.new.title'), new_kurasu_path
%tr
- t_ars(Kurasu, %i[name]).each do |w|
%th= w
%th= t('control')
- @kurasus.each do |k|
%tr
%td= k.name
%td
= link_to t('kurasus.show.title'), kurasu_path(k)
- if @edit_mode
= link_to t('kurasus.edit.title'), edit_kurasu_path(k)
= link_to t('.destroy'), kurasu_path(k), method: :delete, data: {confirm: t('.confirm')}
%title= content_for?(:title) ? yield(:title) : :Attendance
%h1= @title
= yield
ja:
control: 制御
kurasus:
index:
title: クラス一覧
enter_edit_mode: 編集モードに入る
destroy: クラス削除
confirm: クラスを削除してよろしいですか?
show:
title: クラス表示
new:
title: クラス作成
edit:
title: クラス編集
# @param [Object] model モデルクラス
# @param [Array<String>] atr 属性名
def t_ar(model, atr = nil)
if atr
return model.human_attribute_name(atr)
else
return model.model_name.human
end
end
# @param [Object] model モデルクラス
# @param [Array<String>] keys 属性名の配列
# @return [Array<String>] 翻訳後の文字列の配列
def t_ars(model, keys)
keys.map { |key| model.human_attribute_name(key) }
end
ja:
activerecord:
models:
kurasu: クラス
attributes:
kurasu:
name: クラス名
obsolete: 未使用
Kurasus
login by hkob
GET #index
normal mode
behaves like response_status_check
response should be 200
behaves like response_body_includes
response body includes ["クラス一覧:", "小林弘幸", "2300"]
behaves like response_body_not_includes
response body does not include 5300 (FAILED - 1)
obsolete view mode
behaves like response_status_check
response should be 200
behaves like response_body_includes
response body includes ["クラス一覧:", "小林弘幸", "2300", "5300"]
not login
GET #index
behaves like response_status_check
response should be 302
behaves like response_body_not_includes
response body does not include ["クラス一覧:", "小林弘幸", "2300", "5300"]
(中略)
Finished in 0.63085 seconds (files took 0.79338 seconds to load)
7 examples, 1 failure
class KurasusController < ApplicationController
before_action :authenticate_teacher!
def index
@edit_mode = true if params[:edit_mode]
@kurasus = current_teacher.kurasus.order_name
@kurasus = @kurasus.not_obsolete unless @edit_mode
end
end
Kurasus
login by hkob
GET #index
normal mode
behaves like response_status_check
response should be 200
behaves like response_body_includes
response body includes ["クラス一覧:", "小林弘幸", "2300"]
behaves like response_body_not_includes
response body does not include 5300
obsolete view mode
behaves like response_status_check
response should be 200
behaves like response_body_includes
response body includes ["クラス一覧:", "小林弘幸", "2300", "5300"]
not login
GET #index
behaves like response_status_check
response should be 302
behaves like response_body_not_includes
response body does not include ["クラス一覧:", "小林弘幸", "2300", "5300"]
Finished in 0.19479 seconds (files took 0.21901 seconds to load)
7 examples, 0 failures
長くなったので今日はここまで