Exercises
寫一個整合測試,測試密碼重設過期的分支,把以下程式碼的空格(FILL_IN)填上。這段程式碼使用了 response.body,用來回傳頁面的 HTML。有很多方法可以測試過期,下面的程式碼是檢查回傳的 HTML 中有沒有包含「expired」這個字。
test/integration/password_resets_test.rb
require 'test_helper'
class PasswordResetsTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
@user = users(:michael)
end
.
.
.
test "expired token" do
get new_password_reset_path
post password_resets_path, password_reset: { email: @user.email }
@user = assigns(:user)
@user.update_attribute(:reset_sent_at, 3.hours.ago)
patch password_reset_path(@user.reset_token),
email: @user.email,
user: { password: "foobar",
password_confirmation: "foobar" }
assert_response :redirect
follow_redirect!
assert_match /FILL_IN/i, response.body
end
end
目前所有的使用者都會在 /users 中顯示出來,並且可以透過 /users/:id 這樣的 URL 瀏覽,但應該是只有經過啟動的使用者才會顯示在頁面,這樣比較符合邏輯。完成下面的程式碼,來檢查有經過啟動的使用者才會被顯示的行為。下面的程式碼使用了 Active Record 的 where 方法,在下一章我們會學到更多關於這個方法的應用。
附加題:為 /users 和 /users/:id 編寫整合測試。
app/controllers/users_controller.rb
class UsersController < ApplicationController
.
.
.
def index
@users = User.where(activated: FILL_IN).paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
redirect_to root_url and return unless FILL_IN
end
.
.
.
end
注意這邊使用了
and取代&&,兩者的作用基本上一樣,但&&的優先權更高,會和root_url綁定的太緊。我們可以把redirect_to root_url放在括號裡,但慣例上是使用and。
在 app/models/user.rb 中的 activate 和 create_reset_digest 方法都調用了兩次 update_attribute 方法,每一次調用都要單獨執行一個 database transaction。完成下面的程式碼,使用 update_columns 方法來取代兩個 update_attribute 調用,這樣修改後只會觸及 database 一次。然後執行測試,確保測試通過。
app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
.
.
.
# Activates an account.
def activate
update_columns(activated: FILL_IN, activated_at: FILL_IN)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_columns(reset_digest: FILL_IN,
reset_sent_at: FILL_IN)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end