LoginSignup
3
3

More than 1 year has passed since last update.

【Rails】factory-bot、rspecのセットアップや記入法など解説

Last updated at Posted at 2022-01-29

背景

本格的に1人でテストコードを書くとなった際に、セットアップやファイルは何を指しているなど色々と全くわかっていなかったのでその備忘録。

セットアップ

rspecのセットアップ
基本的に公式のREADMEを参考に進めていく。

Gemfile開発環境テスト環境rspec-railsを記入していく。

Gemfile.
group :development, :test do
  gem 'rspec-rails'
end

厳密には開発環境にはrspec-railsは入れなくても良いが、その場合にはRAILS_ENV=test にする必要がある。

bundle  install後、ターミナルにてrails g rspec:installを実行。

ターミナル.
$ bundle install

$ rails g rspec:install
 create .rspec
 create spec
 create spec / spec_helper.rb
 create spec / rails_helper.rb

factory_botのセットアップ
railsのテストでmodelを扱うのに便利なFactoryBot
設定をしておくとuser = create(:user)のような形でDB登録やモデルのビルドができる。

Gemfile.
group :development, :test do
  gem 'factory_bot_rails'
end
spec/rails_helper.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
    #このコードを加えることで、以下のようにrspecのテストコード中でFactory_botのメソッドを使用する際に、クラス名の指定を省略できるようになる
end

# 通常FactoryBotをつけないと、メソッドを呼べない
 user = FactoryBot.create(:user)

 #設定を追加することで、FactoryBotの記述が省略できる。
 user = create(:user)

.rspecファイルに**--require spec_helper**の一文が入っていなければ追記。rails_helperの読み込みを行っている。

..rspecファイル.
--require spec_helper
--format documentation
#format documentationを入れると、RSpecで表示の出力を綺麗にすることができる

ファイルの作成

今回はタスク投稿できるアプリで、taskuserのモデルテストをしていく。
まずターミナルにてtaskuserのモデルのテストファイルを作成する。

ターミナル.
$ rails g rspec:model task
      create  spec/models/task_spec.rb
      invoke  factory_bot
      create  spec/factories/tasks.rb

$ rails g rspec:model user
      create  spec/models/user_spec.rb
      invoke  factory_bot
      create  spec/factories/users.rb

結果ファイル構成はこうなる↓。
Image from Gyazo

テストデータ作成

まずはfactories/users.rbにてテストユーザーの作成をしていく。

users.rb
FactoryBot.define do
  factory :user do
    sequence(:email) {|n| "user_#{n}@example.com"} 
    #↑これが呼び出されるたびに、nの部分に数字が一つずつ増えて入るため、一意性が保たれる
    password {'password'}
    password_confirmation {'password'}
  end
end

次にfactories/tasks.rbにてテストタスクの作成。

tasks.rb
FactoryBot.define do
  factory :task do
    sequence(:title, "title_1")
    #"title_1"とすることで、"title_◯"と数字が増えていく。
        #メール同様一意性が保たれる    
    content {'content'}
    status {:todo}
    deadline {1.week.from_now}
    association :user
  end
end

association :user
例えば下記のようなletを使ったコードがあったとする。

RSpec.describe Task, type: :model do
  let(:user) { FactoryBot.create(:user) }
  let(:task) { FactoryBot.create(:task,user: user) }
end

taskを作成する際に、userも必要なため、user: userと記述する必要があるが、association :userをファクトリに書いておくと、コードがとてもスッキリする。(has_manybelongs_toの関係がuserオブジェクトを自動的に作成してくれる。)

RSpec.describe Task, type: :model do
  let(:user) { create(:user) }
  let(:task) { create(:task,user: user) }
end

↓このように省略できる

RSpec.describe Task, type: :model do
  let(:task) { create(user) }
end

テスト内容ファイル解説

models/task_spec.rbを例に、ファイル作成時にデフォルトで下記の文言が入っている。この解説を先にする。

task_spec.rb
require 'rails_helper'
#spec/rails_helper.rb を読み込む為の設定

RSpec.describe Task, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

RSpec.describe Task, type: :model do ~ end
この間に記入したものが、taskRSpecを書く宣言をしている。

pending "add some examples to (or delete) #{__FILE__}"
pendingというのは保留の意味で、実際にテストを書いていく際にまだテストをせずスキップさせたい際に使われる。

つまりここでは、 "add some examples to (or delete) #{FILE}" というテスト(仮)を保留としている。

このテンプレートが書かれたModelSpecでテスト実行をすると下記が出てくる。

1 example, 0 failures, 1 pending

・1 example
1つのテストがある

・0 failures
失敗したテストは0だった

・1 pending
1つ保留のテストがあった

基本的にデフォルトのpendingは要らないので、コメントアウトしておくなり、削除しておくなりしておく。

テスト内容作成

記入の流れ

specファイルではほとんど、describe/context/itを用いて記述する。

describeにはテスト対象の「クラス」や「メソッド」などを記入。

contextにはテストしようとしている状況「ログイン中のユーザーの場合」などを記入。

itは期待する結果を記入。

大まかな記入の流れは下記。

example.rb
describe "バリデーションについて" do

  context "姓名が設定されている場合 "
    it "姓名が表示されること" do
      # ...
    end

  context "姓名が記入されていない場合"
    it "姓名を入力してくださいとエラーが出るようにする" do
      # ...
    end

    context "..."
    it "..." do
    end
   .
   .
  end

実際に記入する際に使うマッチャ(matcher)について

マッチャ(matcher)は「期待値と実際の値を比較して、一致した(もしくは一致しなかった)という結果を返すオブジェクト」のこと。

describe "四則演算" do
 context "足し算"
   it "1+2の合計は3になること" do
     expect(1 + 2).to eq 3
   end
end

上記のコードを例に見てみると、直感的に内容が分かりやすくなっている。
expect(1 + 2).to eq 3
expectは期待するという意味。
toは、であることを示す。
eqはイコールなので、日本語に当てはめると
(1+2)イコール3となることを期待しますとなり、1+2の合計は3になることと同義になる。

この**eq**の部分がマッチャ(matcher)に該当し、他にも種類がある。
toはマッチャではなくRSpecのメソッド。「〜ではないこと」はnot_to(to_notでも可)と表記できる。

よく使うマッチャを表記しておく。

・空であることを望んでいる
expect(user).to be_empty
# user.empty? が true になればパスする

・存在することを望んでいる
expect(user).to be_valid 
# user.valid? が true になればパスする

・存在しないことを望んでいる/not_to(to_not)を使わない記入法
expect(user).to be_invalid
# user.invalid? が true になればパスする

テスト作成

実際にtaskのテストを記入する。

task_spec.rb
require 'rails_helper'

RSpec.describe Task, type: :model do  
  describe 'validaton' do
    it 'タスクを問題なく作成' do
      task = build(:task)
      expect(task).to be_valid
      expect(task.errors).to be_empty
    end

    it 'タイトル空欄は無効' do
      task = build(:task,title: "")
      expect(task).to be_invalid
      expect(task.errors[:title]).to eq ["can't be blank"]
    end

    it 'ステータス空欄は無効' do
      task_without_status = build(:task,status: nil)
      expect(task_without_status).to be_invalid
      expect(task_without_status.errors[:status]).to eq ["can't be blank"]
    end

    it '同一タイトルは無効' do
      task = create(:task)
      task2 = build(:task, title: task.title)
      expect(task2).to be_invalid
      expect(task2.errors[:title]).to eq ["has already been taken"]
    end

    it '別タイトルは有効' do
      task = create(:task)
      task3 = build(:task,title: "another title")
      expect(task3).to be_valid
      expect(task3.errors).to be_empty
    end
  end
end

task = create(:task)task = build(:task)の違いは、
createだと実際にテーブルのデータが保存され
buildではテーブルにデータが保存されずにテストを実行してくれる。

参考記事

RailsにRSpecを導入する方法とやさしい解説
【Rails】factory_botの使い方メモ
RailsアプリへのRspecとFactory_botの導入手順
rspecを読みやすくメンテしやすく書くために
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」
FactoryBotのassociationとは
【FactoryBot】associationの使い方

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