#はじめに
Railsチュートリアル第6版のテストをRSpecで書き直していく。
###目次
#Minitest
###パスワード再設定のプレビューメソッド(完成)
test/mailers/previews/user_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/account_activation
def account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/password_reset
def password_reset
user = User.first
user.reset_token = User.new_token
UserMailer.password_reset(user)
end
end
###パスワード再設定用メイラーメソッドのテストを追加する
test/mailers/user_mailer_test.rb
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "account_activation" do
user = users(:michael)
user.activation_token = User.new_token
mail = UserMailer.account_activation(user)
assert_equal "Account activation", mail.subject
assert_equal [user.email], mail.to
assert_equal ["noreply@example.com"], mail.from
assert_match user.name, mail.body.encoded
assert_match user.activation_token, mail.body.encoded
assert_match CGI.escape(user.email), mail.body.encoded
end
test "password_reset" do
user = users(:michael)
user.reset_token = User.new_token
mail = UserMailer.password_reset(user)
assert_equal "Password reset", mail.subject
assert_equal [user.email], mail.to
assert_equal ["noreply@example.com"], mail.from
assert_match user.reset_token, mail.body.encoded
assert_match CGI.escape(user.email), mail.body.encoded
end
end
###パスワード再設定の統合テスト
test/integration/password_resets_test.rb
require 'test_helper'
class PasswordResetsTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
@user = users(:michael)
end
test "password resets" do
get new_password_reset_path
assert_template 'password_resets/new'
assert_select 'input[name=?]', 'password_reset[email]'
# メールアドレスが無効
post password_resets_path, params: { password_reset: { email: "" } }
assert_not flash.empty?
assert_template 'password_resets/new'
# メールアドレスが有効
post password_resets_path,
params: { password_reset: { email: @user.email } }
assert_not_equal @user.reset_digest, @user.reload.reset_digest
assert_equal 1, ActionMailer::Base.deliveries.size
assert_not flash.empty?
assert_redirected_to root_url
# パスワード再設定フォームのテスト
user = assigns(:user)
# メールアドレスが無効
get edit_password_reset_path(user.reset_token, email: "")
assert_redirected_to root_url
# 無効なユーザー
user.toggle!(:activated)
get edit_password_reset_path(user.reset_token, email: user.email)
assert_redirected_to root_url
user.toggle!(:activated)
# メールアドレスが有効で、トークンが無効
get edit_password_reset_path('wrong token', email: user.email)
assert_redirected_to root_url
# メールアドレスもトークンも有効
get edit_password_reset_path(user.reset_token, email: user.email)
assert_template 'password_resets/edit'
assert_select "input[name=email][type=hidden][value=?]", user.email
# 無効なパスワードとパスワード確認
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "barquux" } }
assert_select 'div#error_explanation'
# パスワードが空
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "",
password_confirmation: "" } }
assert_select 'div#error_explanation'
# 有効なパスワードとパスワード確認
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "foobaz" } }
assert is_logged_in?
assert_not flash.empty?
assert_redirected_to user
end
end
#RSpec
###パスワード再設定のプレビューメソッド(完成)
spec/mailers/previews/user_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/account_activation
def account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/password_reset
def password_reset
user = User.first
user.reset_token = User.new_token
UserMailer.password_reset(user)
end
end
###パスワード再設定用メイラーメソッドのテストを追加する
spec/mailers/user_mailer_spec.rb
require "rails_helper"
RSpec.describe UserMailer, type: :mailer do
describe "account_activation" do
let(:user) { FactoryBot.create(:user) }
let(:mail) { UserMailer.account_activation(user) }
it "renders the headers" do
expect(mail.subject).to eq("Account activation")
expect(mail.to).to eq([user.email])
expect(mail.from).to eq(["noreply@example.com"])
end
it "renders the body" do
expect(mail.body.encoded).to match(user.name)
expect(mail.body.encoded).to match(user.activation_token)
expect(mail.body.encoded).to match(CGI.escape(user.email))
end
end
describe "password_reset" do
let(:user) { FactoryBot.create(:user) }
let(:mail) { UserMailer.password_reset(user) }
before do
user.reset_token = User.new_token
end
it "renders the headers" do
expect(mail.subject).to eq("Password reset")
expect(mail.to).to eq([user.email])
expect(mail.from).to eq(["noreply@example.com"])
end
it "renders the body" do
expect(mail.body.encoded).to match(user.reset_token)
expect(mail.body.encoded).to match(CGI.escape(user.email))
end
end
end
*"account_activation"のときと違い、user.reset_tokenが自動作成されないので、"renders the headers"と"renders the body"*の両方でuser.reset_tokenを使用する為にbefore do
で作成する。
###パスワード再設定の統合テスト
spec/requests/password_resets_spec.rb
require 'rails_helper'
RSpec.describe "PasswordResets", type: :request do
let(:user) { FactoryBot.create(:user) }
before do
ActionMailer::Base.deliveries.clear
end
it "password resets" do
get new_password_reset_path
expect(response).to have_http_status(:success)
assert_select "input[name=?]", "password_reset[email]"
post password_resets_path, params: { password_reset: { email: "" } }
expect(flash[:danger]).to eq "Email address not found"
expect(response).to have_http_status(:success)
post password_resets_path,
params: { password_reset: { email: user.email } }
expect(user.reset_digest).not_to eq user.reload.reset_digest
expect(ActionMailer::Base.deliveries.size).to eq 1
expect(flash[:info]).to eq "Email sent with password reset instructions"
expect(response).to redirect_to root_url
user.create_reset_digest
get edit_password_reset_path(user.reset_token, email: "")
expect(response).to redirect_to root_url
user.toggle!(:activated)
get edit_password_reset_path(user.reset_token, email: user.email)
expect(response).to redirect_to root_url
user.toggle!(:activated)
get edit_password_reset_path("wrong token", email: user.email)
expect(response).to redirect_to root_url
get edit_password_reset_path(user.reset_token, email: user.email)
expect(response).to have_http_status(:success)
assert_select "input[name=email][type=hidden][value=?]", user.email
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "barquux" } }
assert_select "div#error_explanation"
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "",
password_confirmation: "" } }
assert_select "div#error_explanation"
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "foobaz" } }
expect(is_logged_in?).to be_truthy
expect(flash[:success]).to eq "Password has been reset."
expect(response).to redirect_to user_path(user)
end
end
request specに記述していく。
user.create_reset_digest
でパスワード再設定の属性を設定する。
assert_template
の部分が思いつかずexpect(response).to have_http_status(:success)
で代用しているが、改善の余地あり。