Rspecモデル編 ~ Railsチュートリアル ~

間違っている所や、良い書き方あればコメントくれると喜びます

事前準備

annotate_modelsのインストール

作業効率を上げるために、スキーマをモデルに書き出します。

[Gemfile]
group :development do
  gem 'annotate'
end

GithubのREADME.rdocを参考すれば簡単に導入できます。

bundle install
bundle exec annotate

GitHub - ctran/annotate_models: Annotate Rails classes with schema and routes info

factory_botの設定

[Gemfile]
group :development, :test do
  gem "factory_bot_rails"
end

gemを追加してbundle install

If you're using Rails, add the following configuration to spec/support/factory_bot.rb and be sure to require that file in rails_helper.rb

RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

ドキュメントに指示に従ってspec/support/factory_bot.rbにコードを追加して

rails_helper.rbにrequire 'support/factory_bot.rb'すると

FactoryBot.createがcreateで使えるようになる。

Userモデル

rails g rspec:model user
rails g factory_bot:model user
rails g factory_bot:model microposts

完成コード

# == Schema Information
#
# Table name: @users
#
#  id                :integer          not null, primary key
#  name              :string
#  email             :string
#  created_at        :datetime         not null
#  updated_at        :datetime         not null
#  password_digest   :string
#  remember_digest   :string
#  admin             :boolean          default(FALSE)
#  activation_digest :string
#  activated         :boolean
#  activated_at      :datetime
#  reset_digest      :string
#  reset_sent_at     :datetime
#

# attr_accessor :remember_token, :activation_token, :reset_token
# before_save :downcase_email
# before_create :create_activation_digest
# validates :name, presence: true, length: { maximum: 50 }
# VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
# validates :email, presence: true, length: { maximum: 255 }, uniqueness: { case_sensitive: false }
# validates :email, format: { with: VALID_EMAIL_REGEX }
# validates :password, presence: true, length: { minimum: 6 }, allow_nil: true

# has_many :microposts, dependent: :destroy
# has_many :active_relationships,   class_name: "Relationship",
#                                                               foreign_key: "follower_id",
#                                                               dependent: :destroy
# has_many :passive_relationships,  class_name:  "Relationship",
#                                                                   foreign_key: "followed_id",
#                                                                   dependent:   :destroy

# has_many :following, through: :active_relationships, source: :followed
# has_many :followers, through: :passive_relationships, source: :follower

  

require 'rails_helper'

# create mean FactoryBot.create

RSpec.describe User, type: :model do

  let(:user) { create(:user) }

  context "name length の境界線テスト" do
    it "nameがblank" do
      user.name = ""
      user.valid?
      expect(user.errors[:name]).to include("can't be blank")
    end

    it "nameが50文字" do
      user.name = "a" * 50
      expect(user).to be_valid
    end

    it "nameが51文字" do
      user.name = "a" * 51
      expect(user).to_not be_valid
    end
  end

  context "email length の境界線テスト" do
    it "emailがblank" do
      user.email = ""
      user.valid?
      expect(user.errors[:email]).to include("can't be blank")
    end

    it "emailが255文字" do
      user.email = "a" * 243 + "@example.com" 
      expect(user).to be_valid
    end

    it "emailが256文字" do
      user.email = "a" * 244 + "@example.com" 
      expect(user).to_not be_valid
    end
  end
  
  it "emailの重複は許可しないか" do
    user = create(:user)
    duplicate_user = build(:user, email: user.email)
    expect(duplicate_user).to_not be_valid
  end

  context "emailのformatテスト" do
    it "正しい場合 part1" do
      user.email = "user@example.com"
      expect(user).to be_valid
    end

    it "正しい場合 part2" do
      user.email = "USER@foo.COM"
      expect(user).to be_valid
    end

    it "正しい場合 part3" do
      user.email = "A_US-ER@foo.bar.org"
      expect(user).to be_valid
    end

    it "正しい場合 part4" do
      user.email = "first.last@foo.jp"
      expect(user).to be_valid
    end

    it "正しい場合 part5" do
      user.email = "alice+bob@baz.cn"
      expect(user).to be_valid
    end

    it "間違っている場合 part1" do
      user.email = "user@example,com"
      user.valid?
      expect(user.errors[:email]).to include("is invalid")
    end

    it "間違っている場合 part2" do
      user.email = "user_at_foo.org"
      user.valid?
      expect(user.errors[:email]).to include("is invalid")
    end

    it "間違っている場合 part3" do
      user.email = "user.name@example."
      user.valid?
      expect(user.errors[:email]).to include("is invalid")
    end

    it "間違っている場合 part4" do
      user.email = "foo@bar_baz.com"
      user.valid?
      expect(user.errors[:email]).to include("is invalid")
    end

    it "間違っている場合 part5" do
      user.email = "foo@bar+baz.com"
      user.valid?
      expect(user.errors[:email]).to include("is invalid")
    end
  end

  context "password length の境界線テスト" do
    it "passwordがblankの時はpass" do
      user.password = ""
      expect(user).to be_valid
    end

    it "passwordがnil" do
      user.password = nil
      user.valid?
      expect(user.errors[:password]).to include("can't be blank")
    end

    it "passwordが6文字" do
      user.password = user.password_confirmation = "a" * 6
      expect(user).to be_valid
    end

    it "passwordが5文字" do
      user.password = user.password_confirmation = "a" * 5
      expect(user).to_not be_valid
    end
  end

  it "userが削除されたらmicropostsを削除される" do
    create_num = 10
    user = create(:user, :user_with_microposts, microposts_count: create_num)
    expect { user.destroy }.to change { Micropost.count }.by(-(create_num))
  end
end

micropostsのテストはdocumentをしっかり読み込めば簡単に表現できます。

factory_bot/GETTING_STARTED.md at master · thoughtbot/factory_bot · GitHub

[factories/users.rb]
FactoryBot.define do
  factory :user do
    name { Faker::Name.name }
    sequence(:email) { |n| "rails_tutorial#{format('%03d', n)}@example.com" }
    password { "password" }
    password_confirmation { "password" }

    trait :user_with_microposts do
      transient do
        microposts_count { 5 }
      end

      after(:create) do |user, evaluator|
        create_list(:micropost, evaluator.microposts_count, user: user)
      end
    end
  end
end

  

[factories/microposts.rb]
FactoryBot.define do
  factory :micropost do
    content { Faker::Lorem.sentence }
    association :user
  end
end

Passwordのnilと空白文字のテストで迷ったが下記リンクで理解した。

has_secure_passowordでのpasswordのvalidation | Web Memorandum

RelationshipとmicropostのモデルテストはUserテストが理解できいれば学ぶことはないので省略

参考文献

RSpec Core 3.8 - RSpec Core - RSpec - Relish

Everyday Rails Testing with… by Aaron Sumner [PDF/iPad/Kindle]

File: README — Documentation for rspec-expectations (3.8.2)