4
9

More than 3 years have passed since last update.

devise+ウィザード形式でのユーザ登録のRspecテスト

Posted at

この記事で学べること

ウィザード形式の登録機能のsystemスペック
sessionメソッドを使用したときのcontrollerスペック

概要

get,postアクションのcontrollerスペックについて

このテストに対するコードを解説した記事です

devise+ウィザード形式でのユーザ登録

開発環境

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

コントローラースペック

細かいコントローラースペックのやり方は下記の記事を参考にしてください

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にするための機能があるのでもっと勉強しないといけないと感じました。
少しでも開発の手助けになると嬉しいです😆

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9