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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Capybara

Capybaraは、 Rubyで開発されているWebアプリケーションテストフレームワークです。Webブラウザ不要でブラウザ上のユーザー操作及びJavaScriptの挙動を自動化することができます。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

テスト駆動開発

テスト駆動開発は、 プログラム開発手法の一種で、 プログラムに必要な各機能をテストとして書き、 そのテストが動作する必要最低限な実装を行い コードを洗練させる、といったサイクルを繰り返す手法の事です。

Q&A

解決済

2回答

2199閲覧

【Rspec】【Rails】メール認証を使ったユーザー登録のテストが通らずアドバイスをお願いいたします

Kahoo

総合スコア14

Capybara

Capybaraは、 Rubyで開発されているWebアプリケーションテストフレームワークです。Webブラウザ不要でブラウザ上のユーザー操作及びJavaScriptの挙動を自動化することができます。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

テスト駆動開発

テスト駆動開発は、 プログラム開発手法の一種で、 プログラムに必要な各機能をテストとして書き、 そのテストが動作する必要最低限な実装を行い コードを洗練させる、といったサイクルを繰り返す手法の事です。

0グッド

0クリップ

投稿2021/04/20 07:50

前提・実現したいこと

Ruby on Railsでオリジナルアプリを作成中です。
Railsチュートリアルに倣ってUserモデルを作成し、
Everyday Railsを拾い読みしながらRspecでテストを書いていますが
テストが通らず詰まっています。

解決したいこと/発生している問題・エラーメッセージ

具体的には、メール認証を使ったユーザー登録のテストをシステムスペックで書いていますが、
下記内容でテストが失敗しています。

Failures: 1) Users User CRUD creating a new user creates a new user Failure/Error: visit edit_user_account_activation_path(@user.activation_token, email: @user.email) ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"user_account_activations", :email=>"test@example.com", :id=>nil}, possible unmatched constraints: [:id] Did you mean? edit_user_account_activation_url

possible unmatched constraints: [:id]とありこれが原因だと考えていますが、
ここがなぜnilになってしまうのかがわかりません。

ブラウザで手動で動かしてみると期待通りの動きをしているので、
ルーティングや他のコードが間違っているわけではなさそうだと考えており、
テストの理解が不足していてその書き方が誤っているのだと思うのですが、
原因と修正すべき点や確認すべき事項などアドバイスいただけますと幸いです。

該当のソースコード

spec/system/users_spec.rb

RSpec

1require 'rails_helper' 2 3RSpec.describe 'Users', js: true, type: :system do 4 describe 'User CRUD' do 5 let(:user) { FactoryBot.build(:user) } 6 let(:user_not_activated) { FactoryBot.build(:user, activated: false) } 7 let(:another_user) { FactoryBot.create(:user, email: 'another@example.com') } 8 before do 9 @number_of_users = User.count 10 ActionMailer::Base.deliveries.clear 11 end 12 13 context 'creating a new user' do 14 it 'creates a new user' do 15 visit signup_path 16 fill_in '名前', with: user_not_activated.name 17 fill_in 'メールアドレス', with: user_not_activated.email 18 fill_in 'パスワード', with: user_not_activated.password 19 fill_in 'パスワード(確認)', with: user_not_activated.password_confirmation 20 click_button 'ユーザー登録' 21 22 expect(ActionMailer::Base.deliveries.size).to eq 1 23 24 @user = User.find_by(email: user_not_activated.email) 25 expect(@user).to_not be_activated 26 27 visit edit_user_account_activation_path(@user.activation_token, email: @user.email) 28 @user.reload 29 expect(@user).to be_activated 30 expect(get_me_the_cookie('remember_token')).to_not eq nil 31 expect(page).to have_current_path edit_user_url(@user) 32 expect(page).to have_content 'ようこそ!' 33 34 expect(User.count).to eq @number_of_users + 1 35 end 36 37#以下略

app/models/user.rb

RubyonRails

1class User < ApplicationRecord 2 attr_accessor :remember_token, :activation_token 3 4 before_save :downcase_email 5 before_create :create_activation_digest 6 validates :name, presence: true, length: { maximum: 50 } 7 8 VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+.[a-z]+\z/i 9 validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: true 10 11 VALID_PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)\w{8,12}\z/ 12 has_secure_password 13 validates :password, presence: true, 14 format: { with: VALID_PASSWORD_REGEX, message: 'は半角8~12文字で英大文字・小文字・数字それぞれ1文字以上含む必要があります' }, 15 allow_nil: true 16 17 # 渡された文字列のハッシュ値を返す 18 def self.digest(string) 19 cost = if ActiveModel::SecurePassword.min_cost 20 BCrypt::Engine::MIN_COST 21 else 22 BCrypt::Engine.cost 23 end 24 BCrypt::Password.create(string, cost: cost) 25 end 26 27 # ランダムなトークンを返す 28 def self.new_token 29 SecureRandom.urlsafe_base64 30 end 31 32 # 永続セッションのためにユーザーをデータベースに記憶する 33 def remember 34 self.remember_token = User.new_token 35 update_attribute(:remember_digest, User.digest(remember_token)) 36 end 37 38 # 渡されたトークンがダイジェストと一致したらtrueを返す 39 def authenticated?(attribute, token) 40 digest = send("#{attribute}_digest") 41 return false if digest.nil? 42 43 BCrypt::Password.new(digest).is_password?(token) 44 end 45 46 def forget 47 update_attribute(:remember_digest, nil) 48 end 49 50 private 51 52 def downcase_email 53 email.downcase! 54 end 55 56 def create_activation_digest 57 self.activation_token = User.new_token 58 self.activation_digest = User.digest(activation_token) 59 end 60end 61

app/controllers/users_controller.rb

RubyonRails

1class UsersController < ApplicationController 2 before_action :logged_in_user, only: %i[show edit update destroy] 3 before_action :correct_user, only: %i[edit update destroy] 4 def new 5 @user = User.new 6 return unless logged_in? 7 8 redirect_to root_url 9 end 10 11 def create 12 @user = User.new(user_params) 13 if @user.save 14 UserMailer.account_activation(@user).deliver_now 15 flash[:info] = 'アカウント認証メールを確認して登録を完了してください' 16 redirect_to root_url 17 else 18 render 'new' 19 end 20 end 21 22#以下略

app/controllers/user_account_activations_controller.rb

RubyonRails

1class UserAccountActivationsController < ApplicationController 2 def edit 3 user = User.find_by(email: params[:email]) 4 if user && !user.activated? && user.authenticated?(:activation, params[:id]) 5 user.update_attribute(:activated, true) 6 user.update_attribute(:activated_at, Time.zone.now) 7 log_in user 8 flash[:success] = 'ようこそ!' 9 redirect_to edit_user_url user 10 else 11 flash[:danger] = 'このURLは無効です' 12 redirect_to root_url 13 end 14 end 15end

試したこと

pメソッドで@userの中身を確認しました。

spec/system/users_spec.rb

RSpec

1#これより前省略 2 context 'creating a new user' do 3 it 'creates a new user' do 4 visit signup_path 5 fill_in '名前', with: user_not_activated.name 6 fill_in 'メールアドレス', with: user_not_activated.email 7 fill_in 'パスワード', with: user_not_activated.password 8 fill_in 'パスワード(確認)', with: user_not_activated.password_confirmation 9 click_button 'ユーザー登録' 10 11 @user = User.find_by(email: user_not_activated.email) 12 expect(ActionMailer::Base.deliveries.size).to eq 1 13 expect(@user).to_not be_activated 14 15 p @user.activation_token # <---- デバッグ用に追記 16 p @user # <---- デバッグ用に追記 17 visit edit_user_account_activation_path(@user.activation_token, email: @user.email) 18#これより後省略

その結果、(エラーメッセージにも出ていた通りですが)
@user.activation_tokenの中身がnilとなっており、これが原因だと考えているのですが
なぜnilになってしまうのかがわかりません。

@user.activation_tokenから生成される@user.activation_digestには値が入っていました。
ということは一度は生成されたactivation_tokenの値が消えてしまっている?のでしょうか。

補足情報(FW/ツールのバージョンなど)

環境:ローカル環境

version
macOS Big Sur11.2.3
Ruby3.0.0
Rails6.1.3
Rspec3.10
(rspec-rails)5.0.1
capybara3.26

その他、情報の過不足などございましたらご教示ください。
よろしくお願いいたします。

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

今回はactivation_tokenuser側からではなく送信されたメールの本文から取得することで、
テストをパスすることができました。

具体的には

@user = User.find_by(email: user_not_activated.email) visit edit_user_account_activation_path(@user.activation_token, email: @user.email)

としていた箇所を

mail = ActionMailer::Base.deliveries.last token = mail.body.encoded.match(/(?<=user_account_activations/).+(?=/)/) user = User.find_by(email: user_not_activated.email) visit edit_user_account_activation_path(token, email: user.email)

としました。(全体的に@userにしていた箇所はuserに書き換えました)

アドバイスいただきありがとうございました。

投稿2021/04/22 06:22

Kahoo

総合スコア14

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

Railsチュートリアルに書いてあるとおりです。

なお、activation_tokenは仮の属性でしかないので (11.1)、データベースのユーザーはこの値を実際には持っていません。

そのため、find_byによってデータベースからuserを取得するとactivation_tokenは空です。
RailsチュートリアルのIntegrationTestではassigns(:user)にてコントローラのインスタンス変数を取得しておりました。


では、どう対処するか?というのはちょっとむずかしいです。

なんせsystemテストは実際にブラウザを動かしてテストするわけですから
逆にブラウザで出来ない「メールを開く」は基本できません。

まぁ、capybara-emailを使う事になると思います

投稿2021/04/21 16:30

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kahoo

2021/04/22 02:57

ご回答ありがとうございます。 > そのため、find_byによってデータベースからuserを取得するとactivation_tokenは空です。 なるほど、`find_by`はデータベースからuserを取得してくるのでしたね、初歩的な認識が抜けておりました。 いただいたアドバイスをもとに他の書き方を模索したいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問