はじめに
モデルスペックの作成をしていきましょう。
FactoryBotを使用しテストデータを作成する点も意識します。
FactoryBotとは?
FactoryBotとは、テストデータを作成するためのgemです。Railsでは開発環境、テスト環境、本番環境と使うデータベースを切り離しており、テスト環境ではテストの前提となるデータをテスト実行前に投入し、次のテストを実施するまでにはもとに戻すといったことをしています。
FactoryBotの使い方
- FactoryBotを使うためのファクトリファイルを用意する。
- ファクトリファイルを使ってテスト用のデータベースにテストデータを投入する。
FactoryBotでデータを作成するためのテンプレートをファクトリファイルと呼んだりします。
作業の流れ
- Rspecをドキュメント形式に変更
- FactoryBotの設定
- ファクトリファイルの作成
- モデルスペックの作成
Rspecをドキュメント形式に変更
describe()
、context()
、it()
の各メソッドに渡されるドキュメント文字列を確認するには、ドキュメントフォーマッタを使用します。 メソッドに渡されたドキュメント文字列を見るにも、ドキュメントフォーマッタを使用するので、下記の設定をしましょう。
--format documentation
FactoryBotの設定
FactoryBotの省略設定
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
通常、FactoryBotのデータを呼び出す際は、FactoryBot.create(:user)
のような書き方をします。
上記の設定をすることで、先頭に記述しているFactoryBot.
を省略して create(:user)
だけで記述できるようになります。
ファクトリファイルの作成
FactoryBot.define do
factory :user do
sequence(:email) { |n| "user_#{n}@example.com" }
password { 'password' }
password_confirmation { 'password' }
end
end
sequence
ユニークな値は、シーケンスを使って生成できます。
FactoryBot.define do
factory :post do
sequence(:title, "title_1")
content { 'Content' }
status { 'todo' }
deadline { 1.week.from_now }
association :user
end
end
コードを紐解く
sequence(:title, "title_1")
こちらのsequence
の書き方はString#nextを使った書き方です。変えたい場所が末尾の場合のみ使えます。なのでemail
では使えなかったのですね。
irb(main):022:0> 'title_1'.next
=> "title_2"
irb(main):023:0> 'title_2'.next
=> "title_3"
irb(main):024:0> 'title_3'.next
=> "title_4"
irb(main):025:0> 'user_#{n}@example.com'.next
=> "user_\#{n}@example.con"
下記の書き方と本質的には同じですが、上記のほうがスッキリしていて好ましいでしょう。
sequence(:title) { |n| "title_#{n}" }
モデルスペックの作成
Postモデルのvalidationに関するテストを記述しましょう。
- すべての属性で有効
- titleなしでは無効
- statusがないと無効
- 重複したタイトルでは無効
- 別のタイトルで有効
require 'rails_helper'
RSpec.describe Post, type: :model do
describe 'validation' do
it 'is valid with all attributes' do
post = build(:post)
expect(post).to be_valid
expect(post.errors).to be_empty
end
it 'is invalid without title' do
post_without_title = build(:post, title: "")
expect(task_without_title).to be_invalid
expect(task_without_title.errors[:title]).to eq ["can't be blank"]
end
it 'is invalid without status' do
post_without_status = build(:post, status: nil)
expect(post_without_status).to be_invalid
expect(post_without_status.errors[:status]).to eq ["can't be blank"]
end
it 'is invalid with a duplicate title' do
post = create(:post)
post_with_duplicated_title = build(:post, title: post.title)
expect(post_with_duplicated_title).to be_invalid
expect(post_with_duplicated_title.errors[:title]).to eq ["has already been taken"]
end
it 'is valid with another title' do
post = create(:task)
post_with_another_title = build(:post, title: 'another_title')
expect(post_with_another_title).to be_valid
expect(post_with_another_title.errors).to be_empty
end
end
end
describe
(RSpec.describe
)はテストのグループ化を宣言します。
it do ... end
の中の記述がパスすればそのテストが通るということになります。
expect(X).to eq Y
「XがYに等しいことを期待する」という意味になります。
マッチャについて
expectの文で使っている to
やeq
やbe_〇〇
のような記述をマッチャと言います。マッチャとは英語でmatcherのことなので、「XとYを比較して、自分のルールに合致しているか判断する述語のこと」です。
X
が期待値、Y
が実際の値といういことです。
expect(X) to 〇〇 Y
to
「~であること」を期待します。
eq
期待値と実際の値が「等しい」かどうか検証しています。
be_valid / be_invalid
valid?
メソッドや、invalid?
メソッドをbe_valid
のような形で検証できます。
expect(user).to be_valid # user.valid? が trueになればパスする
be_empty
こちらもempty?
メソッドを間接的に通しているようなイメージです。
expect(user).to be_empty # user.empty? が trueになればパスする
buildメソッドを使っている理由
build
は結論から言ってnew
と同じだと考えて貰って大丈夫でしょう。(古いRailsでは外部キーがつくかどうかの違いがあったみたいです。)
しかしFactoryBotでインスタンスを作成するメソッドはbuild
とcreate
しか用意されていません。
データベースに保存する必要が無いときはbuild
、データベースにデータを保存しインスタンスを永続化させたいときはcreate
を使うようにしましょう。
終わりに
Rspecは書き方がいろいろあるので難しく感じるかもしれませんが、少しずつ慣れていきましょう!
参考記事


コメント