LoginSignup
1
1

More than 1 year has passed since last update.

Rspecまとめ

Last updated at Posted at 2021-10-18

すぐ忘れるので、まとめてみた

Rspecを導入する

Gemfile
group :development, :test do
  gem "rspec-rails"
end
ターミナル
rails g rspec:install
.rspec
# 見やすくするため追加
--format documentation

FactoryBotを導入する

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

Capybaraを導入する

Gemfile
  gem 'capybara'

Rspecを実行する

rails_helperの設定

spec/rails_helper
require 'capybara/rspec'

RSpec.configure do |config|
  config.include Devise::Test::IntegrationHelpers, type: :request    #Deviseのテスト
  config.include FactoryBot::Syntax::Methods    #FactoryBotの利用
end

requrest_spec

requestの導入

ターミナル
# コマンドを打たずに、ファイルを手動で作成しても良い
rails g rspec:request user
top_spec.rb
require 'rails_helper'

RSpec.describe "Tops", type: :request do
  describe "GET /index" do

    before do
      get root_path
    end

    example "topページへアクセスできること"  do
      expect(response).to have_http_status(:success)
    end

    example "ログイン機能が含まれること" do
      expect(response.body).to include("ログイン")
    end
  end

end

Deviseのspec

users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!

  def index
    @user = User.find(current_user.id)
  end
end
spec/factories/user.rb
FactoryBot.define do
  factory :testuser, class: 'User' do
    name { "testuser" }
    sequence(:email) { |n| "testuser#{n}@user.com" }
    password { "foobar" }
  end
end
users_spec.rb
require 'rails_helper'

RSpec.describe "Users", type: :request do
 let(:user) { create(:testuser) }

  describe "GET /index" do
    context "ログインしていない場合" do
      example "ログインページへリダイレクトすること" do
        get users_path
        expect(response).to redirect_to user_session_path
      end
    end

    context "ログインしている場合" do
      before do
        sign_in user
        get users_path
      end

      example "indexページが表示できること" do
        expect(response).to have_http_status(:success)
      end

      example "ユーザー情報が含まれること" do
        expect(response.body).to include(user.name)
        expect(response.body).to include(user.email)
      end
    end
  end

ransackのspec

institutions_controller.rb
class InstitutionsController < ApplicationController
  before_action :set_q

  def index
    max_num = 50
    if @q.result.count > 50
      @institution = @q.result.limit(max_num)
    else
      @institution = @q.result.all
    end
  end

  private

  def set_q
    @q = Institution.ransack(params[:q])
  end
end
app/views/institution/index.html.erb
<div class="institution_index_main">
  <h3>医療機関検索フォーム</h1>
  <%= search_form_for @q, url: institutions_path do |f| %>
    <div class="flex_center">
      <div class="search_form_container">
        <div class="addres_cont_container">
          <%= f.label :address_cont, '住所で検索' %>
          <%= f.search_field :address_cont %>
        </div>
        <div class="name_introduction_cont_container">
          <%= f.label :name_or_introduction_cont, '情報で検索' %>
          <%= f.search_field :name_or_introduction_cont %>
        </div>
      </div>
      <div class="search_submit_container">
        <%= f.submit '検 索', class:"search_submit", id: "institution_search_submit" %>
      </div>
    </div>
  <% end %>
</div>
factories/institution.rb
FactoryBot.define do
  factory :testinstitution, class: "Institution" do
    name { "testinstitution" }
    postcode { 1234567 }
    address { "東京都東京区東京1-2-3東京ビル1F" }
  end
end
institution_spec.rb
# request_specの場合

require 'rails_helper'

RSpec.describe "Institutions", type: :request do
  let!(:institution) { create(:testinstitution) }

  describe "GET /index" do
    before do
      get institutions_path, params: { q: { name_cont: "test"} }
    end

    example "ページを表示できること" do
      expect(response).to have_http_status(:success)
    end

    example "医療機関情報が含まれること" do
      expect(response.body).to include(institution.name)
      expect(response.body).to include(institution.address)
    end
  end
end
spec/features/institution_feature_spec.rb
# feature_specの場合

require 'rails_helper'

RSpec.feature "Institution_Features", type: :feature do
  let!(:institution) { create(:testinstitution) }

  feature "医療機関検索" do
    before do
      visit institutions_path
    end

    scenario "住所に検索語を含むとき" do
      fill_in 'q[address_cont]', with: '東京都'
      find('#institution_search_submit').click
      expect(page.status_code).to eq(200)
      expect(page).to have_content(institution.name)
      expect(page).to have_content(institution.address)
    end

    scenario "住所に検索語を含まないとき" do
      fill_in 'q[address_cont]', with: '大阪府'
      find('#institution_search_submit').click
      expect(page.status_code).to eq(200)
      expect(page).not_to have_content(institution.name)
      expect(page).not_to have_content(institution.address)
    end

    scenario "検索欄が空欄のとき" do
      fill_in 'q[address_cont]', with: ''
      find('#institution_search_submit').click
      expect(page.status_code).to eq(200)
      expect(page).to have_content(institution.name)
      expect(page).to have_content(institution.address)
    end

    scenario "名前に検索語を含むとき" do
      fill_in 'q[name_or_introduction_cont]', with: 'test'
      find('#institution_search_submit').click
      expect(page.status_code).to eq(200)
      expect(page).to have_content(institution.name)
      expect(page).to have_content(institution.address)
    end

    scenario "名前に検索語を含まないとき" do
      fill_in 'q[name_or_introduction_cont]', with: 'example'
      find('#institution_search_submit').click
      expect(page.status_code).to eq(200)
      expect(page).not_to have_content(institution.name)
      expect(page).not_to have_content(institution.address)
    end

    scenario "検索欄が空欄の時" do
      fill_in 'q[name_or_introduction_cont]', with: ''
      find('#institution_search_submit').click
      expect(page.status_code).to eq(200)
      expect(page).to have_content(institution.name)
      expect(page).to have_content(institution.address)
    end
  end
end

ストロングパラメータのspec

users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!

  def update
    @user = User.find(current_user.id)
    if @user.update(user_params)
      redirect_to edit_user_path(current_user), notice: "プロフィール情報を更新しました"
    else
      render "edit"
    end
  end

  private
  def user_params
    params.require(:user).permit(:name, :image)
  end
end
requests/user_spec.rb
require 'rails_helper'

RSpec.describe "Users", type: :request do
  let(:user) { create(:testuser) }

  describe 'POST #create' do
    before do
      sign_in user
    end

    example 'リクエストが成功すること' do
      put user_path(user.id), params: { user: { name: "exampleuser", image: "image.png" } }
      expect(response.status).to eq 302
    end

    example 'ユーザー名が更新されること' do
      expect do
        put user_path(user.id), params: { user: { name: "exampleuser", image: "image.png" } }
      end.to change { User.find(user.id).name }.from('testuser').to('exampleuser')
    end
  end
end

POST PUT DELETEメソッドのspec

requests/institution_spec.rb
require 'rails_helper'

RSpec.describe "Institutions", type: :request do
  let!(:institution) { create(:testinstitution) }
  let(:param) do
    {
      institution: {
        name: 'examplehospital',
        postcode: 9876543,
        prefecture: "福岡県",
        address_city: "福岡市福岡",
        address_street: "4-5-6",
        address_building: "福岡ショッピング5F",
        address: "福岡県福岡市福岡4-5-6福岡ショッピング5F",
        introduction: "整形外科、リハビリテーション科",
        image: "examplehospital.png"
      }
    }
  end

  describe 'POST /create' do
    example 'リクエストが成功すること' do
      post institutions_path, params: param
      expect(response.status).to eq 302
    end

    example '医療機関が登録されること' do
      expect do
        post institutions_path, params: param
      end.to change(Institution, :count).by(1)
    end
  end

  describe 'PUT /update' do
    example 'リクエストが成功すること' do
      put institution_path(institution.id), params: param
      expect(response.status).to eq 302
    end

    example '医療機関情報が更新されること' do
      expect do
        put institution_path(institution.id), params: param
      end.to change { Institution.find(institution.id).name }.from('testinstitution').to('examplehospital')
    end
  end

  describe 'DELETE /destroy' do
    it 'リクエストが成功すること' do
      delete institution_path(institution.id), params: { id: institution.id }
      expect(response.status).to eq 302
    end

    it '医療機関情報が削除されること' do
      expect do
        delete institution_path(institution.id), params: { id: institution.id }
      end.to change(Institution, :count).by(-1)
    end

    it '医療機関一覧にリダイレクトすること' do
      delete institution_path(institution.id), params: { id: institution.id }
      expect(response).to redirect_to(institutions_path)
    end
  end
end

feature_spec

features_user_spec.rb
require 'rails_helper'

RSpec.feature 'Users_Features', type: :feature do
  include Devise::Test::IntegrationHelpers

  describe "user_login" do
    let(:user) { create(:testuser)}

    scenario "新規作成できるか" do
      visit new_user_registration_path
      expect(page).to have_content("新規登録")
      fill_in "user_registration_email", with: "example@example.com"
      fill_in "user_registration_password", with: "foobar"
      fill_in "user_registration_confirm_password", with: "foobar"
      expect do
        click_button "新規登録"
      end.to change(User, :count).by(1)
    end
  end

  describe "link_to_users" do
    let(:user) { create(:testuser) }

    background do
      sign_in user
    end

    scenario 'プロフィール画面からプロフィール編集画面へ遷移できるか' do
      visit users_path
      expect(page).to have_content(user.name)
      expect(page).to have_content(user.email)
      expect(page).to have_content("アカウント情報を編集する")
      expect(page).to have_content("プロフィールを編集する")
      click_link "プロフィールを編集する"
      expect(page).to have_content("ユーザープロフィール編集")
    end
  end
end
model/consultation.rb
class Consultationhour < ApplicationRecord
  belongs_to :institution
  validates :start_time, presence: true, format: { with: /[[0-2]0-9]:[0-5][0-9]/, message: "は[00:00]の形式で入力してください" }
  validates :end_time, presence: true, format: { with: /[[0-2]0-9]:[0-5][0-9]/, message: "は[00:00]の形式で入力してください" }
  validate :before_start_time

  def before_start_time
    if start_time != nil && end_time != nil
      if end_time < start_time
        errors.add(:end_time, "は開始時間よりも後に設定してください")
      end
    end
  end
end
consultation_feature_spec.rb
require 'rails_helper'

RSpec.feature "Consultationhour_Features", type: :feature do
  let(:medicalstaff) { create(:teststaff) }
  let(:institution) { create(:testinstitution) }

  before do
    login_staff(medicalstaff)
  end

  feature "登録内容が正しいとき" do
    before do
      visit institution_path(institution.id)
      click_link "診療時間を登録する"
    end

    scenario "新規作成できるか" do
      fill_in "new_start_time", with: "08:00"
      fill_in "new_end_time", with: "13:00"
      find('.monday_container').all('option')[1].select_option
      find('.tuesday_container').all('option')[1].select_option
      find('.wednesday_container').all('option')[0].select_option
      find('.thursday_container').all('option')[1].select_option
      find('.friday_container').all('option')[1].select_option
      find('.saturday_container').all('option')[1].select_option
      find('.sunday_container').all('option')[2].select_option
      find('.holiday_container').all('option')[1].select_option
      fill_in "new_detail", with: "日曜日は非常勤医師"
      expect do
        click_button "登録する"
      end.to change(Consultationhour, :count).by(1)
      expect(page).to have_content("診療時間を追加しました")
      expect(page).to have_content("日曜日は非常勤医師")
    end
  end

  feature "登録内容が正しくないとき" do
    before do
      visit institution_path(institution.id)
      click_link "診療時間を登録する"
    end

    scenario "未入力エラーメッセージが表示されるか" do
      click_button "登録する"
      expect(page).to have_content("開始時間が入力されていません")
      expect(page).to have_content("開始時間は[00:00]の形式で入力してください")
      expect(page).to have_content("終了時間が入力されていません")
      expect(page).to have_content("終了時間は[00:00]の形式で入力してください")
    end

    scenario "開始時間と終了時間のエラーメッセージが表示されるか" do
      fill_in "new_start_time", with: "13:00"
      fill_in "new_end_time", with: "10:00"
      click_button "登録する"
      expect(page).to have_content("終了時間は開始時間よりも後に設定してください")
    end
  end
end

中間テーブルのテスト

spec/factories/favorite.rb
FactoryBot.define do
  factory :testfavorite, class: "Favorite" do
    user { create(:testuser) }
    institution { create(:institution) }
  end
end
spec/features/user_feature_spec.rb
describe "link_to_users" do
    let!(:user) { create(:testuser) }
    let!(:institution) { create(:testinstitution) }
    let!(:favorite) { create(:testfavorite, user: user, institution: institution) }

    background do
      sign_in user
    end

    scenario 'プロフィール画面からアカウント情報編集画面へ遷移し、プロフィール画面へ戻れるか', js: true do
      visit users_path
      expect(page).to have_content("お気に入り解除")
      find('.like-btn').click
      expect(page).not_to have_content("お気に入り解除")
    end
別)user作成時にfavoriteを作成する方法
spec/factories/user.rb
FactoryBot.define do
  factory :testuser, class: 'User' do
    name { "testuser" }
    sequence(:email) { |n| "testuser#{n}@user.com" }
    password { "foobar" }

    after(:create) do |user|
      create(:testfavorite, user: user, institution: create(:testinstitution) )
    end
  end
end

# traitを使う方法
FactoryBot.define do
  factory :testuser, class: 'User' do
    name { "testuser" }
    sequence(:email) { |n| "testuser#{n}@user.com" }
    password { "foobar" }

    trait :user_favorite do
      after(:create) do |user|
        create(:testfavorite, user: user, institution: create(:testinstitution) )
      end
    end
  end
end

let(:user) { create(:testuser, :user_favorite) }  # favoriteを作る
let(:user) { create(:testuser) }  # favoriteを作らない

Javascriptのリンクをテスト

spec/rails_helper.rb
Capybara.javascript_driver = :selenium_chrome_headless  # 追加
factories/institution.rb
FactoryBot.define do
  factory :testinstitution, class: "Institution" do
    name { "testinstitution" }
    postcode { 1234567 }
    prefecture { "東京都" }
    address_city { "東京区東京" }
    address_street { "1-2-3" }
    address_building { "東京ビル1F" }
    address { "東京都東京区東京1-2-3東京ビル1F" }
  end
end
features/insitutiton_feature_spec.rb
require 'rails_helper'

RSpec.feature "Institution_Features", type: :feature do
  let!(:institution) { create(:testinstitution) }

  scenario "詳細ページへ遷移できるか", js: true do
    visit institutions_path
    find(".institution_index_tbody_tr").click
    expect(page).to have_content("医療機関情報")
    expect(page).to have_content("編 集")
    expect(page).to have_content("削 除")
  end

  scenario "医療機関情報を編集できるか" do
    visit edit_institution_path(institution.id)
    expect(page).to have_content("医療機関情報修正")
    fill_in "institution_name_text", with: "testclinic"
    click_button "更新する"
    expect(page).to have_content("testclinic")
    expect(page).not_to have_content(institution.name)
  end
end

ヘルパーを導入する

spec/support/LoginMacro.rb
module LoginMacro
  def login_staff(staff)
    visit new_medicalstaff_session_path
      fill_in 'medicalstaff_login_email', with: staff.email
      fill_in 'medicalstaff_login_password', with: staff.password
      click_on 'ログイン'
  end
end
spec/rails_helper.rb
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } 
  # 有効化

RSpec.configure do |config|
  config.include LoginMacro    # 追加
  config.include Devise::Test::IntegrationHelpers, type: :request
  config.include FactoryBot::Syntax::Methods
  Capybara.javascript_driver = :selenium_chrome_headless
end
spec/features/institution_feature_spec.rb
RSpec.feature "Institution_Features", type: :feature do
  let!(:institution) { create(:testinstitution) }

  feature "medicalstaffでログインしたとき" do
    let(:medicalstaff) { create(:teststaff) }
    before do
      login_staff(medicalstaff)
    end
    scenario "新規作成画面へ遷移できるか" do
      visit institutions_path
      expect(page).to have_content("医療機関検索フォーム")
      expect(page).to have_content(institution.name)
      click_link "新規作成"
      expect(page).to have_content("医療機関情報新規作成")
    end
  end
end

参考資料

Rspecを導入する

RSpecには表示の出力をキレイにする --format documentation というオプションがある

FactoryBotを導入する

【Rails】RSpecとFactroyBotの導入・設定まとめ
FactoryBotを使う時に覚えておきたい、たった5つのこと
RSpec - FactoryBotを導入し、効率良く意味のあるデータを作成する

Capybaraを導入する

Capybaraチートシート

Rspecを実行する

使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」
Rspecでbindingを使って変数の中身を調べる方法
現場で使えるRSpecパターン集 for Rails App
今日から使える!RSpec 3で追加された8つの新機能

requrest_spec

Rails RSpecの基本 ~Controller編~
Rails5でコントローラのテストをController specからRequest specに移行する

Deviseのspec

[Rails]Request Specでのログインの実施
RSpec初心者のdevise認証システムテスト
Request SpecでDeviseのsing_in userを実施する

ransackのspec

ruby-on-rails - Rspecテストのランサックは常にすべてのインスタンスを返す
【Rails6】RSpecによる検索機能(ransack)の結合テストの実装

ストロングパラメータのspec

Request Specでテストを書くときはStrong Parametersを通る前のパラメーターも意識しましょう

POST PUT DELETEメソッドのspec

Rails5でコントローラのテストをController specからRequest specに移行する

feature_spec

[Rails, devise, RSpec] テストでサインインする
使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」
【RSpec/capybara】fill_inが上手く動作しない
[Rails][RSpec] Capybaraでフォーム入力をシミュレートしてテストする
【Capybara】idもnameもlabelも存在しないセレクトボックスのオプションを選択する

中間テーブルのテスト

rspecのテスト環境で多対多のアソシエーションを一括生成する方法 +α
コントローラー単体テストエラーを解決したいです。
FactoryBotで中間テーブルのテストデータを用意する方法

Javascriptのリンクをテスト

【Rails】JavaScriptが絡むテスト

ヘルパーを導入する

サポートモジュールを使って結合テストの可読性を向上させる方法

1
1
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
1