この記事で学べること
ウィザード形式の登録機能のsystemスペック
sessionメソッドを使用したときのcontrollerスペック
概要
get,postアクションのcontrollerスペックについて
このテストに対するコードを解説した記事です
開発環境
MacOS Mojave 10.14.6
ruby 2.5.1
Rails 5.2.3
RSpec 3.9
- rspec-core 3.9.0
- rspec-expectations 3.9.0
- rspec-mocks 3.9.0
- rspec-rails 3.9.0
- rspec-support 3.9.0
mysql Ver 14.14 Distrib 5.6.43, for osx10.14
group :development, :test do
gem 'factory_bot_rails'
gem 'pry-byebug'
gem 'rspec-rails'
gem 'rails-controller-testing'
gem 'spring-commands-rspec'
end
group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'webdrivers'
end
rspecの細かい設定は各自調べてください
テストするコード
signups_controller.rb
class SignupsController < ApplicationController
def step1
@user = User.new
end
def step1_validates #step1で入力された情報のバリデーションチャックをするためのアクション
# createアクションにデータを渡すためにsessionに代入
session[:nickname] = user_params[:nickname]
session[:email] = user_params[:email]
session[:password] = user_params[:password_confirmation]
session[:password_confirmation] = user_params[:password_confirmation]
session[:lastname] = user_params[:lastname]
session[:firstname] = user_params[:firstname]
session[:lastname_kana] = user_params[:lastname_kana]
session[:firstname_kana] = user_params[:firstname_kana]
session[:birthday_year] = user_params[:birthday_year]
session[:birthday_month] = user_params[:birthday_month]
session[:birthday_day] = user_params[:birthday_day]
# バリデーションチャックするためにインスタンスを作成
@user = User.new(
nickname: session[:nickname],
email: session[:email],
password: session[:password_confirmation],
password_confirmation: session[:password_confirmation],
lastname: session[:lastname],
firstname: session[:firstname],
lastname_kana: session[:lastname_kana],
firstname_kana: session[:firstname_kana],
birthday_year: session[:birthday_year],
birthday_month: session[:birthday_month],
birthday_day: session[:birthday_day]
)
#バリデーションチャック
@user.valid? #無効な値の場合はfalseとインスタンスに対してエラーメッセージを追加してくれる
#今回はstep1でphonenumberを入力しないので設定しているpresence: trueに引っかかっている
#このメソッドでphonenumberのエラー内容を削除してバリデーションを通過させる。メソッドはprivateにあります
skip_phonenumber_validate(@user.errors)
# verify_recaptcha~~ はロボット確認の為のメソッドで今回は殆ど関係ないです。詳しくはgem 'recaptcha'で調べてください
if verify_recaptcha(model: @user, message: "選択してください") && @user.errors.messages.blank? && @user.errors.details.blank?
redirect_to step2_signups_path #step1で入力したデータにバリデーションエラーがない場合はstep2に遷移する
else
@user.errors.messages[:birthday_day] = change_birthday_validate_message(@user) #エラーメッセージを変更するメソッドで今回は関係ないので説明は省略します
render :step1
end
end
def step2
#入力フォームのためのインスタンスを作成
@user = User.new
end
def create
@user = User.new(
#step1で入力したデータはsessionを使う
nickname: session[:nickname],
email: session[:email],
password: session[:password_confirmation],
password_confirmation: session[:password_confirmation],
lastname: session[:lastname],
firstname: session[:firstname],
lastname_kana: session[:lastname_kana],
firstname_kana: session[:firstname_kana],
birthday_year: session[:birthday_year],
birthday_month: session[:birthday_month],
birthday_day: session[:birthday_day],
#step2で入力したデータはparamsを使う
phonenumber: user_params[:phonenumber]
)
if @user.save
sign_in User.find(@user.id) unless user_signed_in? #保存できたらログイン状態にする
#sessionはもう使用しないので削除する
session.delete(:nickname)
session.delete(:email)
session.delete(:password)
session.delete(:password_confirmation)
session.delete(:lastname)
session.delete(:firstname)
session.delete(:lastname_kana)
session.delete(:firstname_kana)
session.delete(:birthday_year)
session.delete(:birthday_month)
session.delete(:birthday_day)
redirect_to new_address_path
else
render :step2
end
end
private
def user_params
params.require(:user).permit(:nickname, :email, :password, :password_confirmation, :lastname, :firstname,
:lastname_kana, :firstname_kana, :birthday_year,
:birthday_month, :birthday_day, :phonenumber)
end
# 電話番号はstep1で入力しないのでバリデーションエラーがあったときにスキップさせるためのメソッド
# step1で入力しないカラムのバリデーションエラーメッセージを削除する。
def skip_phonenumber_validate(errors)
errors.messages.delete(:phonenumber) #stepの回数や入力するデータに合わせて変更してください
errors.details.delete(:phonenumber)
end
# 生年月日のどれかにひとつでもバリデーションエラーがあった場合は同じエラーメッセージを表示する
def change_birthday_validate_message(user)
if user.errors.messages[:birthday_year].any? || user.errors.messages[:birthday_month].any? || user.errors.messages[:birthday_day].any?
user.errors.messages.delete(:birthday_year)
user.errors.messages.delete(:birthday_month)
user.errors.messages[:birthday_year] = ["生年月日は正しく入力してください"]
user.errors.messages[:birthday_year]
end
end
end
Factory_Bot
spec/factories/users.rb
FactoryBot.define do
factory :user do
nickname { "tanegashiman" }
email { "kkk@gmail.com" }
password { "123456a" }
password_confirmation { "123456a" }
lastname { "向井" }
firstname { "治" }
lastname_kana { "ムカイ" }
firstname_kana { "オサム" }
birthday_year { 2018 }
birthday_month { 12 }
birthday_day { 31 }
phonenumber { "09000000000" }
end
end
コントローラースペック
細かいコントローラースペックのやり方は下記の記事を参考にしてください
- 著書「RSpecによるRailsのテスト入門 テスト駆動開発の習得に向けた実践的アプローチ」著:Aaron Summer氏、訳:伊藤淳一氏、秋元利春氏、魚振江氏、2017年5月4日。
- Qiita「使えるRSpec入門・その1『RSpecの基本的な構文や便利な機能を理解する』」
- Qiita「使えるRSpec入門・その2『使用頻度の高いマッチャを使いこなす』」
spec/controllers/signups_controller_spec.rb
require 'rails_helper'
RSpec.describe SignupsController, type: :controller do
describe "get #step1" do
it "レスポンスが成功すること" do
get :step1
expect(response).to be_successful
end
#200レスポンスは成功というステート
it "200レスポンスを返すこと" do
get :step1
expect(response).to have_http_status "200"
end
end
describe "#step1_validates" do
context "有効なデータの場合" do
# 同じ処理はsubjectを使うことでdryにし同じデータを使っているとわかりやすくする
subject {
user_params = attributes_for(:user)
#post :アクション名, params: { パラメータ名: "値"}, session: { session名: "値" } でデータを指定できる
post :step1_validates, params: { user: user_params},
session: {
nickname: "tanegashiman",
email: "kkk@gmail.com",
password: "123456a",
password_confirmation: "123456a",
lastname: "向井",
firstname: "治",
lastname_kana: "ムカイ",
firstname_kana: "オサム",
birthday_year: 2018,
birthday_month: 12,
birthday_day: 31
}
}
#200レスポンスはリダイレクト成功というステート
it "302レスポオンスを返すこと" do
#設定したsubjectはこのように使う
subject
expect(response).to have_http_status "302"
end
it "電話番号確認ページにリダイレクトすること" do
subject
expect(response).to redirect_to step2_signups_path
end
end
context "無効なデータの場合" do
subject {
#無効なデータのときは処理の流れを理解しているとスムーズに進む
user_params = attributes_for(:user, nickname: "")
post :step1_validates, params: { user: user_params},
session: {
nickname: '',
email: "kkk@gmail.com",
password: "123456a",
password_confirmation: "123456a",
lastname: "向井",
firstname: "治",
lastname_kana: "ムカイ",
firstname_kana: "オサム",
birthday_year: 2018,
birthday_month: 12,
birthday_day: 31
}
}
it "200レスポオンスを返すこと" do
subject
expect(response).to have_http_status "200"
end
it "step1にrenderすること" do
subject
expect(response).to render_template :step1
end
end
end
describe "get #step2" do
before do
# ここで定義しないとsessionがないと怒られる
user_params = attributes_for(:user)
session[:nickname] = user_params[:nickname]
session[:email] = user_params[:email]
session[:password] = user_params[:password_confirmation]
session[:password_confirmation] = user_params[:password_confirmation]
session[:lastname] = user_params[:lastname]
session[:firstname] = user_params[:firstname]
session[:lastname_kana] = user_params[:lastname_kana]
session[:firstname_kana] = user_params[:firstname_kana]
session[:birthday_year] = user_params[:birthday_year]
session[:birthday_month] = user_params[:birthday_month]
session[:birthday_day] = user_params[:birthday_day]
end
it "レスポンスが成功すること" do
get :step2
expect(response).to be_successful
end
it "200レスポンスを返すこと" do
get :step2
expect(response).to have_http_status "200"
end
end
describe "create" do
context "有効なデータの場合" do
subject {
user_params = attributes_for(:user)
post :create, params: { user: user_params},
session: {
nickname: "tanegashiman",
email: "kkk@gmail.com",
password: "123456a",
password_confirmation: "123456a",
lastname: "向井",
firstname: "治",
lastname_kana: "ムカイ",
firstname_kana: "オサム",
birthday_year: 2018,
birthday_month: 12,
birthday_day: 31
}
}
it "302レスポンスを返すこと" do
subject
expect(response).to have_http_status "302"
end
it "送付先登録ページにリダイレクトされること" do
subject
expect(response).to redirect_to new_address_path
end
it "保存することができる" do
user_params = attributes_for(:user)
expect do
subject
end.to change { User.count }.by(1)
end
end
context "無効なデータの場合" do
subject {
user_params = attributes_for(:user)
post :create, params: { user: user_params},
session: {
nickname: "",
email: "kkk@gmail.com",
password: "123456a",
password_confirmation: "123456a",
lastname: "向井",
firstname: "治",
lastname_kana: "ムカイ",
firstname_kana: "オサム",
birthday_year: 2018,
birthday_month: 12,
birthday_day: 31
}
}
it "200レスポンスを返すこと" do
subject
expect(response).to have_http_status "200"
end
it "step2にrenderされること" do
subject
expect(response).to render_template :step2
end
it "保存することができない" do
expect do
subject
end.to change { User.count }.by(0)
end
end
end
end
難しいテストはしてないのでほとんど説明はないです。ぼくはsessionの渡し方と処理の流れを理解できてなくて結構時間がかかりました。
システムスペック
基本的にgem 'capybara'を使っています!!
細かいシステムスペックのやり方は下記の記事を参考にしてください
spec/system/users_spec.rb
require 'rails_helper'
RSpec.describe 'Users', type: :system do
scenario 'ユーザ登録することができる' do
visit step1_signups_path
expect(page).to have_content '会員情報入力' #正しいページにアクセスできているか確認
fill_in 'nickname', with: 'test' #inputタグに入力してる fill_in 'タグのid', with: "入力したい値"
fill_in 'email', with: 'kkk@gmail.com'
fill_in 'password', with: '123456a'
fill_in 'user_lastname', with: '向井'
fill_in 'user_firstname', with: '治'
fill_in 'user_lastname_kana', with: 'ムカイ'
fill_in 'user_firstname_kana', with: 'オサム'
select 1998, from: 'user_birthday_year' #selectタグを選択 select "値', form: "タグのid"
select 12, from: 'user_birthday_month'
select 31, from: 'user_birthday_day'
click_button '次へ進む'
expect(page).to have_content '電話番号の確認' #ウィザード形式なので正しいページに遷移しているか確認
expect do
fill_in 'phonenumber', with: '0000000000'
click_button '次へ進む'
end.to change { User.count }.by(1) #保存されているか確認
expect(page).to have_content '住所入力' #保存後に正しいページに遷移しているか確認
end
end
最後に
今回の記事はあまり説明することがなかったですが、調べると様々マッチャがあったり、適度にDRYにするための機能があるのでもっと勉強しないといけないと感じました。
少しでも開発の手助けになると嬉しいです😆