📑

[RSpec]RSpecの基本③実際に記述してみる。

2023/03/26に公開

今日はRSpec基礎第3回目です。


RSpecを実際に書いてみよう!やってみた!解説

1. RSpecをインストールする

gemを使用することができるのでgemを入れる。

今回使用していくのは、

  • gem 'rspec-rails'
  • gem 'factory_bot_rails'

RSpecはテストの時のみ使用するので、このテストで使用するgemは group :test doの中に記述すること。
gem fileに行けばこのように、group :test doの中にこのような様々なgemの記述はあるから、今回は使用しないので削除してもおk!

ここに書き加えよう。

以下のように!

group :test do
  # Adds support for Capybara system testing and selenium driver
  #gem 'capybara', '>= 3.26'
  #gem 'selenium-webdriver', '>= 4.0.0.rc1'
  # Easy installation and use of web drivers to run system tests with browsers
  #gem 'webdrivers'
  gem 'rspec-rails'
  gem 'factory_bot_rails'
end

補足:
消したgemは以下のように、defaltであるシステムテストを書くために使用されるgemです。
今回は他のを入れるので、消しても良いのです!

  • capybara:
    Railsアプリケーションをブラウザのようにテストできるツール。
    (ブラウザで実際に起こるであろうことをシミュレートして、テストを書くことができる。)
  • selenium-webdriver:
    Webブラウザを自動化するためのオープンソースのフレームワーク。
    ブラウザで実際に起こるであろうことをシミュレートして、テストを書くことができる。
  • webdrivers:
    上記したSeleniumとCapybaraのための自動インストールおよび自動更新機能を提供するGem。
    Webドライバーを自動的にインストールして、ブラウザでテストを実行することができます。
  • bundle install する
  • RSpecをアプリケーションにインストールする. rails g rspec:install

このようにspecフォルダーが作成される!!!


補足:gem'factory_bot_rails'についてと使用方法

RSpecのテストデータを生成するライブラリで、便利なメソッドを提供してくれるgem。

使用方法について

使用方法

  1. gemに記入し、bundle installrails g rspec:installでインストール。
  2. インストールまでしたら、[spec/rails_helper.rbファイル]に以下のコードを追記していく。
# spec/rails_helper.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

=> これで、「factory_bot_rails」が使えるようになる。

  1. モデルごとに,spec/factoriesディレクトリ以下にファクトリーファイルを作成

    ex. )
  • 3-1. specの下にfactoriesディレクトリを作成
  • 3-2. そのfactoriesディレクトリの中にファイルを作成する
              Postモデルの場合であれば、users.rbを作成.
  • 3-3. ファイルの中に以下のように、デフォルトで使用する属性を定義していく。

ここからは、また以降作成方法の流れに沿って解説していきます!!!


今回はPOSTモデルについて(結合テスト)作成していきます。

2. ファイルを作成し、テストを書いていく

先ほどのコマンドでspecフォルダが作成されたので...
テストを記述するファイルをspecフォルダに作成していく。

postモデルのRSpecを作るにあたって、userも必要になるので作成していきます。

2-1. まずは使用するgem factory_bot_railsの設定

これを記述することによって、このgemを使用できるようになる。

# spec/rails_helper.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

2-2. specの下にfactoriesディレクトリを作成

2-3. そのfactoriesディレクトリの中に,テストデータ生成用のファイルを作成

  • まずはpostについて作っていくためにUSERが必要なので、userを作成
  • ファクトリーファイルでは、デフォルトで使用する属性を定義していく。

    定型文
FactoryBot.define do
  factory :モデル名 do
    ~属性の定義~
  end
end
spec/factories/user.rb
FactoryBot.define do
  factory :user do
    first_name { Faker::Lorem.characters(number: 10) }
    last_name { Faker::Lorem.characters(number: 10) }
    first_name_kana { Faker::Lorem.characters(number: 10) }
    last_name_kana { Faker::Lorem.characters(number: 10) }
    email { Faker::Internet.email }
    password { "password" }
    password_confirmation { "password" }

    after(:build) do |user|
      user.image.attach(io: File.open("spec/images/no_image.png"), filename: "no_image.png", content_type: "application/xlsx")
    end
  end
end

  • buildメソッド:
    FactoryBotで定義されたモデルオブジェクトのインスタンスを生成するメソッド。
    RSpecでテストを書く際に、データベースにデータを作成することなく、モデルオブジェクトを生成するために使用される。
  • after(:build) do |user| ~ end
    buildの後に実行する処理を定義するためのメソッドであり、RSpecのコールバックの一つ。
    今回はこのようにafter(:build)~の形で使用。写真がuserモデルにはActive Strageで登録するので、userが作成された後に入れると定義している!

ではこの感じでpostも作成していきます。

テーブルは以下のような感じです。

spec/factories/post.rb
FactoryBot.define do
  factory :post do
    post_status { 0 }
    title { Faker::Lorem.characters(number: 20) }
    body { Faker::Lorem.characters(number: 100) }
    user
  end
end

2-4. テスト用のfileを作成していく

今回テストするpostについてのfileを作成しましょう。
前回RSpecのディレクトリについて書きましたので、
トグルに一応短縮でまとめて入れておくのでわからなかったらみること!

RSpecのディレクトリについて
project/
├── app/
├── config/
├── lib/
└── spec/  *RSpecでテストコードを格納するためのディレクトリ
    ├── controllers/  *コントローラーに対するテストコードを置くディレクトリ
    ├── models/      *モデルに対するテストコードを置くディレクトリ
    ├── features/    *機能に対するテストコードを置くディレクトリ
    ├── support/     *テストコードで使用するサポートファイルを置くためのディレクトリ
    │   ├── helpers/    *RSpecのテストで使用するヘルパーメソッドを定義する場所
    │   └── matchers/   *独自のマッチャーを定義するための場所
    └── spec_helper.rb  *spec/ ディレクトリ以下のテストスイートで共通の設定を行う場所

  • modelフォルダーを作成し、その中にpost_spec.rbを作成する。

ちなみに、モデルの状態としてはこのような感じです。

app/model/post.rb
class Post < ApplicationRecord
 has_many :favorites, dependent: :destroy
 has_many :post_comments, dependent: :destroy
 belongs_to :user
 #hashtag機能
 has_many :post_hashtags, dependent: :destroy
 has_many :hashtags, through: :post_hashtags

 #ActiveStorageの設定
 has_one_attached :post_image
 #validation
 validates :title, length: { minimum: 2 }
 validates :body, length: { minimum: 2 }

 #下書き、公開のenum設定
 enum post_status: { published: 0, draft: 1 }

 #下書き機能
 def save_draft
   self.post_status = :draft
   save(validate: false)
 end

 #利用停止のuserの投稿を閲覧不可に。
 scope :published, -> { where(post_status: :published) }
 scope :by_active_users, -> { joins(:user).where(users: { user_status: 0 }) }
 scope :visible, -> { published.by_active_users }

 #いいね機能
 def favorited_by?(user)
   #favorites.exists?(user_id: user.id)
   user.present? && favorites.exists?(user_id: user.id)
 end

 #検索用
 def self.search_content(content, method)
   if method == "perfect"
     where(title: content)
   elsif method == "partial"
     where("title LIKE ?", "%#{content}%")
   else
     all
   end
 end
end

ではこれをテストに変換していきます。

spec/models/post_spec.rb
require "rails_helper"

RSpec.describe Post, type: :model do
  subject { post.valid? }
  # Factoryでデータを作る
  let(:user) { create(:user) }
  let(:post) { build(:post, user_id: user.id) }

  
  it "テスト用のUserやPostが存在するか(Factoryでちゃんと作られたか確認" do
    expect(user).to be_valid
    expect(post).to be_valid
  end

  context "titleカラム" do
    it "空欄でないこと" do
      post.title = ""
      is_expected.to eq false
    end
    it "2文字以上であること: 1文字は×" do
      post.title = Faker::Lorem.characters(number: 1)
      is_expected.to eq false
    end
    it "2文字以上であること: 2文字は〇" do
      post.title = Faker::Lorem.characters(number: 2)
      is_expected.to eq true
    end
  end

  describe "Accociations" do
    it "PostはUserが一つのみ (belongs_to test)" do
      t = Post.reflect_on_association(:user)
      expect(t.macro).to eq(:belongs_to)
    end

    it "PostはPost Commentが複数(has_many test)" do
      t = Post.reflect_on_association(:post_comments)
      expect(t.macro).to eq(:has_many)
    end
  end

  describe "Instance methods" do
    describe "#save_draft" do
      it "saves the post as draft" do
        post.update(post_status: :published)
        post.save_draft
        expect(post.reload.post_status).to eq("draft")
      end
    end
  end

  describe "Scopes" do
    describe ".published" do
      let!(:published_post) { create(:post, post_status: :published) }
      let!(:draft_post) { create(:post, post_status: :draft) }

      it "returns published posts" do
        expect(Post.published).to eq([published_post])
      end
    end

    describe ".by_active_users" do
      let!(:active_user) { create(:user, user_status: 0) }
      let!(:inactive_user) { create(:user, user_status: 1) }
      let!(:active_user_post) { create(:post, user: active_user) }
      let!(:inactive_user_post) { create(:post, user: inactive_user) }

      it "returns posts by active users" do
        expect(Post.by_active_users).to eq([active_user_post])
      end
    end

    describe ".visible" do
      let!(:visible_post) { create(:post, post_status: :published, user: create(:user, user_status: 0)) }
      let!(:invisible_post1) { create(:post, post_status: :draft, user: create(:user, user_status: 0)) }
      let!(:invisible_post2) { create(:post, post_status: :published, user: create(:user, user_status: 1)) }

      it "returns visible posts" do
        expect(Post.visible).to eq([visible_post])
      end
    end
  end
end


3. テストの実行

  • 実行コマンド:  rspec spec/テストのファイル名

今回はこのようになりますね。 rspec spec/models/post_spec.rb

何事もエラーなく成功するとこのようになります。


初めてやったときに失敗したこと

  • 実行するtestmが結合テストだった場合に、関連のあるものの定義がしていないと行うことができない
    と言うことを考えていなかった。
    => もとになるところから作成していこう

  • 作成したファイルにrequire "rails_helperが表記していなかった
    => 以下のようなエラーでずっと進まない。笑

NameError:
  uninitialized constant Post

  RSpec.describe Post, type: :model do
                 ^^^^
# ./spec/models/post.rb:1:in `<top (required)>'
No examples found.

Discussion