RailsチュートリアルのテストをRspecで書いてみた[第12章]
はじめに
今回はRspecの学習の一環として、RailsチュートリアルのテストをRspecで書いていきます。
至らない点があるかもしれませんが、その際はコメントにてご指摘をお願いします。
各種バージョン
Ruby 2.7.0
Rails 6.0.3.3
Rspec 3.9
Capybara 3.33.0
Factory_bot_rails 6.1.0
第12章
リスト12.12: パスワード再設定用メイラーメソッドのテストを追加する
spec/mailers/user_mailer_spec.rb require "rails_helper" RSpec.describe UserMailer, type: :mailer do let(:user) { FactoryBot.create(:user) } describe "account_activation" do 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 # 下の一文を忘れないこと!! before { user.reset_token = User.new_token } let(:mail) { UserMailer.password_reset(user) } 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
今回はrailsチュートリアルにあるようにuser.reset_token = User.new_token を行っています。
activation_tokenと違い、before_createで生成されていないからです。
リスト12.18: パスワード再設定の統合テスト
注意!!
Railsチュートリアルではassignsメソッドを使用してありますが、現在は非推奨になっています。
その為、assignsメソッドなしで書こうと思いましたが、結局Userモデル内にあるメソッドをそのまま利用しております。
(テストとしては駄目な気しかしないけど、他に思いつかない...)
models/user.rb # パスワード再設定の属性を設定する def create_reset_digest self.reset_token = User.new_token update(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now) end
以下が今回書いたテストです。
spec/requests/password_resets_request_spec.rb require 'rails_helper' RSpec.describe "PasswordResets", type: :request do let(:user) { FactoryBot.create(:user) } # edit,updateアクションへのテストで必要です。 before { user.create_reset_digest } describe "def new" do it "returns http success" do get "/password_resets/new" aggregate_failures do expect(response).to have_http_status(:success) expect(response.body).to include 'Forgot password' end end end describe "def create" do # メールアドレスが無効 it 'falis create with invalid email' do post password_resets_path, params: { password_reset: { email: "" } } aggregate_failures do expect(response).to have_http_status(200) expect(response.body).to include 'Forgot password' end end # メールアドレスが有効 it 'succeds create with valid email' do post password_resets_path, params: { password_reset: { email: user.email } } aggregate_failures do expect(user.reset_digest).not_to eq user.reload.reset_digest expect(ActionMailer::Base.deliveries.size).to eq 1 expect(response).to redirect_to root_url end end end describe "def edit" do # メールアドレスが無効 context 'when user sends correct token and wrong email' do before { get edit_password_reset_path(user.reset_token, email: "") } it 'fails' do expect(response).to redirect_to root_url end end # 無効なユーザ context 'when not activated user sends correct token and email' do before do # toggle!は真偽値を反対にして、破壊的メソッドなので保存します。 user.toggle!(:activated) get edit_password_reset_path(user.reset_token, email: user.email) end it 'fails' do expect(response).to redirect_to root_url end end # メールアドレスが有効で、トークンが無効 context 'when user sends wrong token and correct email' do before { get edit_password_reset_path("wrong", email: user.email) } it 'fails' do expect(response).to redirect_to root_url end end # メールアドレスもトークンも有効 context 'when user sends correct token and email' do before { get edit_password_reset_path(user.reset_token, email: user.email) } it 'succeeds' do aggregate_failures do expect(response).to have_http_status(200) expect(response.body).to include "Reset password" end end end end describe "def update" do # 無効なパスワードとパスワード確認 context "when user sends wrong password" do before do patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "barquux", }, } end it 'fails' do aggregate_failures do expect(response).to have_http_status(200) expect(response.body).to include "Reset password" end end end # パスワードが空 context "when user sends blank password" do before do patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "", password_confirmation: "", }, } end it 'fails' do aggregate_failures do expect(response).to have_http_status(200) expect(response.body).to include "Reset password" end end end # 有効なパスワードとパスワード確認 context "when user sends correct password" do before do patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "foobaz", }, } end it 'fails' do aggregate_failures do expect(is_logged_in?).to be_truthy expect(response).to redirect_to user end end end end end
リスト12.21: パスワード再設定の期限切れのテスト
spec/requests/password_resets_request_spec.rb require 'rails_helper' RSpec.describe "PasswordResets", type: :request do let(:user) { FactoryBot.create(:user) } # 今回のテストでも必要です。 before { user.create_reset_digest } ・・・ describe "def check_expiration" do context "when user updates after 3 hours" do before do user.update_attribute(:reset_sent_at, 3.hours.ago) patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobar", password_confirmation: "foobar", }, } end it "fails" do expect(response).to redirect_to new_password_reset_url end end end end
リスト12.22: パスワード再設定が成功したらダイジェストをnilにする
spec/requests/password_resets_request_spec.rb require 'rails_helper' RSpec.describe "PasswordResets", type: :request do let(:user) { FactoryBot.create(:user) } before { user.create_reset_digest } ・・・ describe "def update" do ・・・ # 有効なパスワードとパスワード確認 context "when user sends correct password" do before do patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "foobaz", }, } end it 'fails' do aggregate_failures do expect(is_logged_in?).to be_truthy # 下の一文を追加する。 expect(user.reload.reset_digest).to eq nil expect(response).to redirect_to user end end end end