モデルの単体テストの書き方


はじめに

みなさん単体テストは好きですか??
私は苦手です。。。。

多分苦手な方も多いと思うので、今回はモデルの単体テストの書き方について紹介できればいいかなと思います。
私も未熟なので、参考程度にしてください。おねがいします。。

1.Gemfile

はじめにgemfileにrspecを導入していきます。
今回はfactory_botも使うので、以下のようにgemfileに記述していきましょう。

group :development, :test do
  gem 'rspec-rails', '~> 3.8'
  gem 'factory_bot_rails', '~> 5.0'
end

bundle installも忘れずに!

$ bundle install

2.RSpecの設定

ターミナルで以下のコマンドを記述。
specというディレクトリが生成されます。

$ rails g rspec:install

.rspecに以下を記述

--format documentation

テスト用のDBを最新の構成にする

$ rails db:migrate:reset RAILS_ENV=test

3.モデルを作成し単体テストを書いていこう

まずはテストモデルを生成します。

$ rails generate rspec:model モデル名

それではfactory_botを使ってモデルにテストデータを予め準備します。
spec/factories/モデル名.rbファイルを作成しましょう。
ファイルの中身の模範は以下のようです。


FactoryBot.define do

  factory :モデル名 do
    カラム名     {"仮の値"}
  end

end

実際に書くとこんな感じ。

FactoryBot.define do

  factory :product do
    name            {Faker::Games::Zelda.item}
    infomation      {Faker::Games::Zelda.game}
    category_id     {"1"}
    brand           {Faker::Games::Zelda.location}
    status_id       {"1"}
    delivery_id     {"1"}
    area_id         {"1"}
    day_id          {"1"}
    price           {Faker::Number.number(digits: 4)}
    situation       {"1"}
    user
  end

end

続いて、factory_botの情報を使ってテストコードを書いていきます。
spec/models/モデル名_spec.rbファイルを作成しましょう。
ファイルの中身の模範はこんな感じ。


require 'rails_helper'

describe モデル名 do
  describe '#メソッド名' do
    context 'グループ分けの説明' do
      it 'テストの説明' do
        テスト内容
      end
    end
  end
end

実際はこんな感じ。
factory_botを使用しているので、記述量が減って簡単にかけます!


require 'rails_helper'

describe Product do
  describe '#create' do

    context '出品できる場合' do
      it "productとproduct_imageがある場合は保存できること" do
        expect(build(:product, product_images: [build(:product_image)])).to be_valid
      end
    end

    context '出品できない場合' do
      it "product_imageがない場合は保存できないこと" do
        product = build(:product)
        product.valid?
        expect(product.errors[:product_images]).to include("can't be blank")
      end

      it "nameがない場合は保存できないこと" do
        product = build(:product, name: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:name]).to include("can't be blank")
      end

      it "infomationがない場合は保存できないこと" do
        product = build(:product, infomation: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:infomation]).to include("can't be blank")
      end

      it "category_idがない場合は保存できないこと" do
        product = build(:product, category_id: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:category_id]).to include()
      end

      it "status_idがない場合は保存できないこと" do
        product = build(:product, status_id: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:status_id]).to include()
      end

      it "delivery_idがない場合は保存できないこと" do
        product = build(:product, delivery_id: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:delivery_id]).to include()

      end

      it "area_idがない場合は保存できないこと" do
        product = build(:product, area_id: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:area_id]).to include()
      end

      it "day_idがない場合は保存できないこと" do
        product = build(:product, day_id: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:day_id]).to include()
      end

      it "priceがない場合は保存できないこと" do
        product = build(:product, price: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:price]).to include()
      end

      it "priceが300以下の場合は保存できないこと" do
        product = build(:product, price: "299", product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:price]).to include()
      end

      it "priceが9,999,999以上の場合は保存できないこと" do
        product = build(:product, price: "10000000", product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:price]).to include()
      end

      it "user_idがない場合は保存できないこと" do
        product = build(:product, user_id: nil, product_images: [build(:product_image)])
        product.valid?
        expect(product.errors[:user_id]).to include()
      end
    end
  end
end


最後に以下のコマンドをターミナルで実行し、テストがうまくいっているか確認です!

$ bundle exec rspec spec/models/モデル名_spec.rb

以上です!