質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.05%

rspecを用いたコントローラーのテストにおいて、DBにuser登録するテストが通らない。 新規ユーザー登録実装方法:devise/ウィザード形式

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 450

shuya-tamaru

score 6

Ruby on railsにてECサイトを開発中。
deviseを用いて新規ユーザー登録をウィザード形式で実装(1ページ目 ユーザー情報 → 2ページ目 携帯電話 → 3ページ目 住所 → 4ページ目 クレジッド →
登録完了!)実装は完了済み。
新規登録のcontrollerテストをrspecを用いて記述中。
4ページ目のcreate_cardメソッドにおいて、どうしてもDBにユーザーが登録されるテストが通らない。
原因はsessionがらみかなとはと思うのですが・・・ググってもなかなか解決できず、初心者で基本的なことかもしれず申し訳ありませんがご教授願います。

Post #create_card
    card is valid
      リクエストは302 リダイレクトとなること (FAILED - 1)
      データベースに新しいユーザーが登録されること (FAILED - 2)

Failures:

  1) Users::RegistrationsController Post #create_card card is valid リクエストは302 リダイレクトとなること
     Failure/Error: expect(response.status).to eq 302

       expected: 302
            got: 200

       (compared using ==)
     # ./spec/controllers/users/registrations_controller_spec.rb:160:in `block (4 levels) in <top (required)>'

  2) Users::RegistrationsController Post #create_card card is valid データベースに新しいユーザーが登録されること
     Failure/Error:
       expect do
         subject
       end.to change{User.count}.by(1)

       expected `User.count` to have changed by 1, but was changed by 0
     # ./spec/controllers/users/registrations_controller_spec.rb:169:in `block (4 levels) in <top (required)>'

Finished in 0.3602 seconds (files took 1.8 seconds to load)
17 examples, 2 failures

Failed examples:

rspec ./spec/controllers/users/registrations_controller_spec.rb:157 # Users::RegistrationsController Post #create_card card is valid リクエストは302 リダイレクトとなること
rspec ./spec/controllers/users/registrations_controller_spec.rb:168 # Users::RegistrationsController Post #create_card card is valid データベースに新しいユーザーが登録されること

registrations_controller.rb↓

require 'rails_helper'

describe Users::RegistrationsController do

  describe 'Post #create_card' do

#途中のテストは省略

    context 'card is valid' do
      before do
        @request.env["devise.mapping"] = Devise.mappings[:user]
        @user = attributes_for(:user)
        session["devise.regist_data"] = {user: @user}
        @cellphone = attributes_for(:cellphone)
        @address = attributes_for(:address)
        @card = attributes_for(:card)
      end

      subject {
        post :create_card, params: {card: @card },
        session: {
          user: @user,
          cellphone: @cellphone,
          address: @address
        }
      }

      it 'リクエストは302 リダイレクトとなること' do
        post :create_card, params: {card: @card}
        subject
        expect(response.status).to eq 302
      end

      it 'データベースに新しいユーザーが登録されること' do
        expect do
          subject
        end.to change{User.count}.by(1)
      end
    end
  end
end

registrations_controller.rb

# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
  before_action :configure_sign_up_params, only: [:create]
  # before_action :configure_account_update_params, only: [:update]

  # GET /resource/sign_up

  def new
    @user = User.new
  end

  # POST /resource
  def create
    @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new and return
    end
    session["devise.regist_data"] = {user: @user.attributes}
    session["devise.regist_data"][:user]["password"] = params[:user][:password]
    @cellphone = @user.build_cellphone
    render :new_cellphone
  end

  def create_cellphone
    @user = User.new(session["devise.regist_data"]["user"])
    @cellphone = Cellphone.new(cellphone_params)
    unless @cellphone.valid?
      flash.now[:alert] = @cellphone.errors.full_messages
      render :new_cellphone and return
    end
    @user.build_cellphone(@cellphone.attributes)
    session["cellphone"] = @cellphone.attributes
    @address = @user.build_address
    render :new_address
  end

  def create_address
    @user = User.new(session["devise.regist_data"]["user"])
    @cellphone = Cellphone.new(session["cellphone"])
    @address = Address.new(address_params)
    unless @address.valid?
      flash.now[:alert] = @address.errors.full_messages
      render :new_address and return
    end
    @user.build_cellphone(@cellphone.attributes)
    @user.build_address(@address.attributes)
    session["address"] = @address.attributes
    @card = @user.build_card
    render :new_card
  end


  def create_card
    @user = User.new(session["devise.regist_data"]["user"])
    @cellphone = Cellphone.new(session["cellphone"])
    @address = Address.new(session["address"])
    @card = Card.new(card_params)
    unless @card.valid?
      flash.now[:alert] = @card.errors.full_messages
      render :new_card and return
    end
    @user.build_cellphone(@cellphone.attributes)
    @user.build_address(@address.attributes)
    @user.build_card(@card.attributes)
    @user.save
    sign_in(:user, @user)
  end 

  # GET /resource/edit
  # def edit
  #   super
  # end

  # PUT /resource
  # def update
  #   super
  # end

  # DELETE /resource
  # def destroy
  #   super
  # end

  # GET /resource/cancel
  # Forces the session data which is usually expired after sign
  # in to be expired now. This is useful if the user wants to
  # cancel oauth signing in/up in the middle of the process,
  # removing all OAuth session data.
  # def cancel
  #   super
  # end

  protected

  # If you have extra params to permit, append them to the sanitizer.
  def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
  end

  def cellphone_params
    params.require(:cellphone).permit(:number)
  end

  def address_params
    params.require(:address).permit(:zip_code, :prefecture, :city, :address, :building, :phone_tell)
  end

  def card_params
    params.require(:card).permit(:number, :validated_date_year, :validated_date_month, :security_number)
  end



  # If you have extra params to permit, append them to the sanitizer.
  # def configure_account_update_params
  #   devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
  # end

  # The path used after sign up.
  # def after_sign_up_path_for(resource)
  #   super(resource)
  # end

  # The path used after sign up for inactive accounts.
  # def after_inactive_sign_up_path_for(resource)
  #   super(resource)
  # end
end

rails_helper.rb

# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'

require File.expand_path('../config/environment', __dir__)

# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!

# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }

# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  puts e.to_s.strip
  exit 1
end
RSpec.configure do |config|
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.include FactoryBot::Syntax::Methods
  # config.include Devise::TestHelpers, type: :controller
  config.include Devise::Test::ControllerHelpers, type: :controller
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

  # RSpec Rails can automatically mix in different behaviours to your tests
  # based on their file location, for example enabling you to call `get` and
  # `post` in specs under `spec/controllers`.
  #
  # You can disable this behaviour by removing the line below, and instead
  # explicitly tag your specs with their type, e.g.:
  #
  #     RSpec.describe UsersController, :type => :controller do
  #       # ...
  #     end
  #
  # The different available types are documented in the features, such as in
  # https://relishapp.com/rspec/rspec-rails/docs
  config.infer_spec_type_from_file_location!

  # Filter lines from Rails gems in backtraces.
  config.filter_rails_from_backtrace!
  # arbitrary gems may also be filtered via:
  # config.filter_gems_from_backtrace("gem name")
end
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

# Users::RegistrationsController#create_card
def create_card
  @user = User.new(session["devise.regist_data"]["user"])
  @cellphone = Cellphone.new(session["cellphone"])
  @address = Address.new(session["address"])
  @card = Card.new(card_params)
  unless @card.valid?
    flash.now[:alert] = @card.errors.full_messages
    render :new_card and return
  end
  @user.build_cellphone(@cellphone.attributes)
  @user.build_address(@address.attributes)
  @user.build_card(@card.attributes)
  @user.save
  sign_in(:user, @user)
end 

そもそもコントローラでリダイレクトするように書かれていないように見受けられます。
sign_in(:user, @user)部分でリダイレクトされているのであればその部分を追記してください。
また、このコードで実際に画面で動かした時は正しく動作していますか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/14 00:56

    ありがとうございます!
    開発環境では問題なく正しく動作してます!
    いただいた回答を参考に、sign_in @userを入れたことで、とりあえず一歩進んだような気はします。

    現状、2つのテストが同様のエラー
    RuntimeError:
    Could not find a valid mapping for

    になったので、今度はこれを解決していこうと思います!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Rubyに関する質問
  • rspecを用いたコントローラーのテストにおいて、DBにuser登録するテストが通らない。 新規ユーザー登録実装方法:devise/ウィザード形式