1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

商品出品画面を作る④RSpec(テスト)

Last updated at Posted at 2020-04-12

#RSpecを使用し、バリデーションのテストとコントローラーのテストを行う
商品出品画面を作る①へ

モデル

item.rb
  validate :images_presence
  validates :name, :text, :category_id, :condition_id, :deliverycost_id, :pref_id, :delivery_days_id, :boughtflg_id,:user_id, presence: true
  validates :price, presence: true, numericality: { greater_than_or_equal_to: 300, less_than_or_equal_to: 9999999 }

  has_many_attached :images
  belongs_to :user
  belongs_to :category
  
  def images_presence
    if images.attached?
      # inputに保持されているimagesがあるかを確認
      if images.length > 10
        errors.add(:image, '10枚まで投稿できます')
      end
    else
      errors.add(:image, '画像がありません')
    end
  end

コントローラー

items.controller.rb
  def new
    @item = Item.new
    @category_parent =  Category.where("ancestry is null")
  end

  def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to root_path
     
    else
      render :new
    end
  end


  private

  def item_params
    params.require(:item).permit(:name, :text, :category_id, :condition_id, :deliverycost_id, :pref_id, :delivery_days_id, :price, images: []).merge(user_id: current_user.id, boughtflg_id:"1")
  end
end

これらのテストを行う

#FactoryBotで必要なデータを用意する

###商品

factory/items.rb
FactoryBot.define do
  factory :item do
    name             {"hoge"}
    text             {Faker::Lorem.sentence}
    condition_id     {1}
    deliverycost_id  {1}
    pref_id          {13}
    delivery_days_id {1}
    price            {"9999"}
    boughtflg_id     {1}

    # アソシエーション
    user
    category

    # 1枚の画像をアップロードする
    trait :image do
      after(:build) do |item|
        File.open("#{Rails.root}/spec/images/Unknown.jpeg") do |f|
          item.images.attach(io: f, filename: "Unknown.jpeg", content_type: 'image/jpeg')
        end
      end
    end
    
    # 11枚の画像をアップロードする
    trait :images do
      after(:build) do |item|
        11.times do
          File.open("#{Rails.root}/spec/images/Unknown.jpeg") do |f|
            item.images.attach(io: f, filename: "Unknown.jpeg", content_type: 'image/jpeg')
          end
        end
      end
    end
  end
end

trait :image:テストで呼び出す事で1枚の画像をアップロードできる
trait :images:テストで呼び出す事で11枚の画像をアップロードできる
アップロードの方法はActiveStorageの方法に準ずる
specディレクトリ以下にimagesディレクトリを用意し、その中にテストで使う画像を用意する

###ユーザー

factory/users.rb
FactoryBot.define do
  factory :user do
    password = Faker::Internet.password(min_length: 7)
    nickname {"まーたろー"}
    lastname {"真"} 
    firstname {"太郎"}
    lastname_kana {"ま"}
    firstname_kana {"たろう"}
    zipcode {"1111111"}
    pref_id {"1"}
    city {"青梅市"}
    address {"1-2-3"}
    buildingname {"マンション"}
    birthyear_id {"20"}
    birthmonth_id {"5"}
    birthday_id {"23"}

    email {Faker::Internet.free_email}
    password {password}
    password_confirmation {password}
  end
end

###カテゴリー

factory/categories.rb
FactoryBot.define do
  factory :category do
    name {"hoge"}
    ancestry {"1/99"}
  end
end

カテゴリーは内容が少ないのでcategoryで用意しなくてもよかったかも・・・

#モデルのテスト
モデルはいろんな人が資料をだしているので、簡潔に画像部分だけ

supec/models/item_spec.rb
RSpec.describe Item, type: :model do
  describe '#create' do
    let(:user){create(:user)}
    let(:category){create(:category)}

    context '保存できない' do
      it '画像がない'do
        item = build(:item, user: user, category: category)
        item.valid?
        expect(item.errors[:image]).to include('画像がありません')
      end
      it '画像が多い'do
        item = build(:item, :images, user: user, category: category)
        item.valid?
        expect(item.errors[:image]).to include('10枚まで投稿できます')
      end

###ポイント
*let(:user){create(:user)}let(:category){create(:category)}*でこのテストで使うユーザーとカテゴリーの情報を作成(create)する
factory :itemのtraitで用意した画像を呼び出すかどうかを決める
画像がない👉呼び出さない
画像が多い👉11枚の画像をアップロードする方を呼び出す
呼び出す時はbuild()の第二引数に指定する

#テスト実行

ターミナル
 bundle exec rspec ./spec/models/item_spec.rb

###バリデーションが無い時
スクリーンショット 2020-04-12 20.34.43.png

###include()が空の時
スクリーンショット 2020-04-12 20.35.54.png

#コントローラーのテスト

supec/controllers/items_controller_spec.rb
require 'rails_helper'

RSpec.describe ItemsController, type: :controller do
  let(:user) { create(:user) }
  let(:category) { create(:category) }
  # テストで使用するitemに紐つくuserとcategoryを先にcreateする

  describe 'GET #new' do
    context 'ログインしている' do
      it "@itemという変数が正しく定義されている" do
        login user
        #テストするユーザ情報をログイン状態にする
        get :new
        #HTTPメソッド:get でnewアクションを動かす
        expect(assigns(:item)).to be_a_new(Item)
      end
      it '出品ページが表示される' do
        login user
        get :new
        expect(response).to render_template :new
      end
    end
    context 'ログインしていない' do
      it 'ログインページに遷移する' do
        get :new
        expect(response).to redirect_to(new_user_session_path)
      end
    end
  end

newアクションのテスト

  • ログインしている➡︎@item = Item.newが期待通りか?
     :itemfactory :itemで用意したデータを呼び出している
     Itemはコントローラーの『@item = Item.new』を動かした時の@item
     つまり、テストでは
     expect(自分が用意したitemのデータ).to be_a_new(newしたインスタンス変数)
     であり、コントローラーで言うと
     newしたインスタンス変数 = 自分が用意したitemのデータ
     となる

  • ログインしている➡︎正しいページが表示されるか?
     割愛

  • ログイン指定ない➡︎ログインページに遷移するか?
     割愛

supec/controllers/items_controller_spec.rb
  describe 'POST #create' do
    let(:image) { fixture_file_upload('spec/images/Unknown.jpeg','image/jpeg' )}
    # 画像をアプロードする行動を再現する
    let(:item) { { item: attributes_for(:item, user_id: user.id, category_id: category.id )}}
    # 画像以外のitemの配列を作成
    let(:item_image) { { item: attributes_for(:item, user_id: user.id, category_id: category.id ,images: [image])}}
    # 画像を含めたのitemの配列を作成
    context 'ログインしている' do
      subject { post :create, params: item_image}
      context '保存できる' do
        it 'レコードの数が増える' do
          login user
          expect{subject}.to change(Item, :count).by(1)
        end
        it 'トップページにリダイレクトする' do
          login user
          subject
          expect(response).to redirect_to(root_path)
        end
      end
      context '保存できない' do
        subject { post :create, params: item}
        it 'レコードの数が変わらない' do
          login user
          expect{subject}.to_not change(Item, :count)
        end
        it '出品画面に戻る' do
          login user
          subject
          expect(response).to render_template :new
        end
      end
    end
    context 'ログインしていない' do
      it 'ログインページに遷移する' do
        post :create, params: item
        expect(response).to redirect_to(new_user_session_path)
      end
    end
  end

createアクションのテスト

  • ログインしている➡︎保存できる➡︎レコードの数が増える
    expect{subject}.to change(Item, :count).by(1)
    subject➡︎直前でまとめたsubject { post :create, params: item_image}
    post :create➡︎HTTPメソッド:post でcreateアクションを動かす
    params: item_image➡︎**let(:item_image)**を呼び出す
    change(Item, :count).by(1)➡︎Itemモデルの数が1増えるか?

  • ログインしている➡︎保存できる➡︎トップページにリダイレクトする
    expect(response).to redirect_to(root_path)
    response➡︎createアクションを動かした後に表示される画面
    redirect_to(root_path)➡︎root_path(トップページ)にリダイレクトするか?

  • ログインしている➡︎保存できない➡︎レコードの数が変わらない

  • ログインしている➡︎保存できない➡︎出品画面に戻る

  • ログインしていない➡︎ログインページに遷移する

#テスト実行

ターミナル
 bundle exec rspec ./spec/controllers/items_controller_spec.rb

#ポイント

  • テストは実際にコントローラやモデルを動かしているのでbinding.pryを使って止めて確かめられる
  • ActiveStorageを使用しているのでfactoryで画像を準備する際にはActiveStorageのやり方に準ずる必要がある。
  • コントローラーテスト中の用意はRSpecのやり方でOK
  • 画像の扱いが出来れば大体できる!
  • let(:item_image) { { item: attributes_for(:item, user_id: user.id, category_id: category.id ,images: [image])}}
    の部分はbinding.pryを使って形をよく確かめる!!
1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?