Programming Journal

学習したことの整理用です。

【RSpec】 管理者・一般ユーザーを分けてテストデータを作成する

実装したいこと

管理者と一般ユーザーで権限が違うため、それぞれのテストデータを作成したい。

trait

トレイトを使う。 属性を予めセットして定義できる機能。

trait:A distinguishing quality or characteristic, typically one belonging to a person.

FactoryBot.define do
  factory :user do
    sequence(:name) { |n| "general-#{n}" } #sequenceで一意のデータを生成する。
    password { 'password' }
    password_confirmation { 'password' }
    role { :general } #default値を設定する。

    trait :admin do
      sequence(:name) { |n| "admin-#{n}" }
      role{ :admin }
    end

    trait :general do
      sequence(:name) { |n| "general-#{n}" }
      role{ :general }
    end
  end
end

traitの使い方がいまいち理解できてなかったのですが、
デフォルトでは全ての属性をセットしておく。→ traitは指定したい属性だけ切り取ってセットする。
っていうことですね。

マクロを使ってログイン機能だけセットする

module LoginMacro
  def login_as(user)
    visit login_path
    fill_in 'user[name]', with: user.name

    click_button '次へ'
    fill_in 'user[password]', with: 'password' #user.passwordはnil
    click_button 'ログイン'
  end
end

rails_helper.rbで読み込み設定をしておきます。

(略)
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } #support以下を読み込む

RSpec.configure do |config|
(略)
  config.include FactoryBot::Syntax::Methods #FacctoryBotの記述を省略できる
  config.include LoginMacros #LoginMacrosを読み込む
end

module ? macros?

以前もログイン機能の切り出しはしたのですが、そのときはLoginModuleとしていました。
Macro Moduleどっち?…と疑問に思ったので講師の方に質問したところ、慣例で現場によって異なるのでどっちでもいいとのことでした。

System Spec

require 'rails_helper'

RSpec.describe "AdiminArticlesPreviews", type: :system do
  let(:admin) { create :user, :admin }
  describe '記事作成画面で画像ブロックを追加' do
    context '画像なしで投稿する' do
      it 'プレビューが正常に表示される' do
        login_as(admin)
    (略)
        switch_to_window(windows.last) #windowを切り替え、最後のwindowを指定
        expect(page).to have_content('test')
      end
    end
  end
end

let(:admin) { create :user, :admin }
let(定義名) { 内容 }です。
{ create :user, :admin }:admin部分で、さっき定義したtraitを使っています。


login_as(admin)
マクロで定義したログイン用のメソッドを使っています。
引数に、letで作成したadminを渡して、管理者としてログインしています。

System Specの命名について

Controllerが、admin/articles/previews_controllerのように名前空間で分かれているとき、どうやってファイル名をつけたらいいのかな?って思ったけど、普通に、admin_articles_previews_spec.rbとするみたい。

NGコード

最初に私が実装したコードを反省用に残しておく。一応動くけど酷い、汚い。。
describe,contextbeforeをなんとなくで使っているので勉強します

require 'rails_helper'

RSpec.describe "Previews", type: :system do
  let(:admin) { create(:user, :admin) }
  before{ login_as(admin) }

  describe '記事の画像投稿機能' do
    before do
      click_link '記事'
      click_link '新規作成'
      fill_in 'タイトル',  with: 'test'
      click_button '登録する'
      click_link 'ブロックを追加する'
      click_link '画像'
    end

    context '画像なしで投稿する' do
      it 'プレビューが見れること' do
        click_link 'プレビュー'
        switch_to_window(windows.last)
        expect(page).to have_content('test')
      end
    end
  end
end

参考

トレイトについて 『everydayRails』P64

マクロについて
マクロ(ヘルパーメソッド)を定義してフィーチャースペックのユーザー切替えを楽に行う - Qiita