#はじめに
Railsチュートリアル第6版のテストをRSpecで書き直していく。
###目次
#Minitest
###fixtureのユーザーを有効にしておく
michael:
name: Michael Example
email: michael@example.com
password_digest: <%= User.digest('password') %>
admin: true
activated: true
activated_at: <%= Time.zone.now %>
archer:
name: Sterling Archer
email: duchess@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
lana:
name: Lana Kane
email: hands@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
malory:
name: Malory Archer
email: boss@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
<% 30.times do |n| %>
user_<%= n %>:
name: <%= "User #{n}" %>
email: <%= "user-#{n}@example.com" %>
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
<% end %>
###アカウント有効化のプレビューメソッド
# 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
UserMailer.password_reset
end
end
###Userメイラーのテスト
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
end
###Userテスト内の抽象化したauthenticated?メソッド
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar")
end
.
.
.
test "authenticated? should return false for a user with nil digest" do
assert_not @user.authenticated?(:remember, '')
end
end
###ユーザー登録のテストにアカウント有効化を追加する
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password" } }
end
assert_equal 1, ActionMailer::Base.deliveries.size
user = assigns(:user)
assert_not user.activated?
# 有効化していない状態でログインしてみる
log_in_as(user)
assert_not is_logged_in?
# 有効化トークンが不正な場合
get edit_account_activation_path("invalid token", email: user.email)
assert_not is_logged_in?
# トークンは正しいがメールアドレスが無効な場合
get edit_account_activation_path(user.activation_token, email: 'wrong')
assert_not is_logged_in?
# 有効化トークンが正しい場合
get edit_account_activation_path(user.activation_token, email: user.email)
assert user.reload.activated?
follow_redirect!
assert_template 'users/show'
assert is_logged_in?
end
end
#RSpec
###factoryのユーザーを有効にしておく
FactoryBot.define do
factory :user do
name {"Michael Example"}
sequence(:email) { |n| "test#{n}@example.com" }
password {"password"}
password_confirmation {"password"}
admin {true}
activated {true}
activated_at {Time.zone.now}
trait :noadmin do
admin {false}
end
trait :noactivated do
admin {false}
activated {false}
activated_at { nil }
end
trait :users do
sequence(:name) { |n| "User#{n}" }
sequence(:email) { |n| "testusers#{n}@example.com" }
admin {false}
end
end
end
userのactivatedをtrueにしておく。
また、ユーザー登録のテストにアカウント有効化を追加するにおいて使用する無効なユーザーであるnoactivatedユーザーを作成しておく。
###アカウント有効化のプレビューメソッド
# 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
UserMailer.password_reset
end
end
###Userメイラーのテスト
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
end
FactoryBotでユーザーを作成した際、Userモデルのbefore_create :create_activation_digest
が実行されるため、user.activation_token = User.new_token
は記述しなくて良い。
###Userテスト内の抽象化したauthenticated?メソッド
require 'rails_helper'
RSpec.describe User, type: :model do
before do
@user = User.new(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar")
end
.
.
.
it "authenticated? should return false for a user with nil digest" do
expect(@user.authenticated?(:remember, "")).to be_falsy
end
end
###ユーザー登録のテストにアカウント有効化を追加する
require 'rails_helper'
RSpec.describe "UsersSignups", type: :request do
before do
ActionMailer::Base.deliveries.clear
end
let(:user) { FactoryBot.attributes_for(:user) }
let(:noactivateduser) { FactoryBot.create(:user, :noactivated) }
it "valid signup information" do
expect {
post users_path, params: { user: user }
}.to change(User, :count).by(1)
expect(response).to redirect_to root_url
expect(is_logged_in?).to be_falsy
end
it "valid signup information with account activation" do
expect(noactivateduser).to_not be_activated
log_in_as(noactivateduser)
expect(is_logged_in?).to be_falsy
get edit_account_activation_path("invalid token", email: noactivateduser.email)
expect(is_logged_in?).to be_falsy
get edit_account_activation_path(noactivateduser.activation_token, email: "wrong")
expect(is_logged_in?).to be_falsy
get edit_account_activation_path(noactivateduser.activation_token, email: noactivateduser.email)
assert noactivateduser.reload.activated?
expect(noactivateduser).to be_activated
expect(response).to redirect_to user_path(noactivateduser)
expect(is_logged_in?).to be_truthy
end
end
第8章において作成したrequest specに記述していく。
第8章では、is logges_in?
メソッドを使用するためにsystem specの一部をrequest specの*"valid signup information"に移動させたが、その中身を今回の内容に合わせて一部変更している。
注意すべきなのが、"valid signup information"*を成功させるためには、FactoryBot.attributes_for
でユーザーを作成する必要があるが、実際に、無効なユーザーのデータを使用するためにはFactoryBot.create
で作成していなければならないため、今回は二つのテストに分けて記述している。