RSpec入門:インストールからモデルスペックの書き方まで
はじめに
RSpecはRubyのテストフレームワークで、Railsプロジェクトで最もよく使われています。describe・context・itを使って「何をテストするか」を自然言語に近い形で書けるのが特徴です。
この記事ではrspec-railsのインストールから基本的な書き方、モデルスペックの例まで解説します。
インストール
Gemfileに追加してインストールします。
# Gemfile group :development, :test do gem 'rspec-rails' gem 'factory_bot_rails' # テストデータの作成に使う(推奨) end
bundle install bin/rails generate rspec:install
rspec:install で以下のファイルが生成されます。
.rspec spec/ spec/spec_helper.rb spec/rails_helper.rb
.rspec にデフォルトのオプションを書いておくと便利です。
--require spec_helper --format documentation
基本構造
RSpec.describe User, type: :model do context "名前がある場合" do it "有効である" do user = User.new(name: "田中太郎", email: "tanaka@example.com") expect(user).to be_valid end end context "名前がない場合" do it "無効である" do user = User.new(name: nil, email: "tanaka@example.com") expect(user).not_to be_valid end end end
describe— テスト対象(クラスや機能)を宣言するcontext— テストする条件や状況を説明するit— 期待する動作を1つ書く(1つのitに1つのテスト)expect(...).to— 期待値を検証する
よく使うマッチャー
# 等値 expect(result).to eq(42) expect(result).not_to eq(0) # 真偽 expect(user).to be_valid expect(user.admin?).to be true expect(user.guest?).to be false # nil チェック expect(value).to be_nil expect(value).not_to be_nil # 型チェック expect(result).to be_a(String) expect(result).to be_an(Array) # 包含 expect(array).to include("apple") expect(string).to include("hello") # 例外 expect { method_call }.to raise_error(ArgumentError) expect { method_call }.to raise_error(ArgumentError, "invalid argument") # 変化 expect { user.save }.to change(User, :count).by(1) expect { user.destroy }.to change(User, :count).by(-1) expect { user.update(name: "新しい名前") }.to change { user.name }.from("旧名前").to("新しい名前")
let と before
let — 遅延評価でデータを定義する
RSpec.describe User, type: :model do let(:user) { User.new(name: "田中太郎", email: "tanaka@example.com") } it "有効である" do expect(user).to be_valid end end
let は最初に呼ばれたときに評価され、同じテスト内では同じオブジェクトが返ります。let! は各テストの前に必ず評価されます。詳しくは「RSpecのletとlet!:遅延評価と即時評価の違いと使い分け」を参照してください。
before — テストの前処理
RSpec.describe User, type: :model do before do @user = User.create(name: "田中太郎", email: "tanaka@example.com") end it "名前を持つ" do expect(@user.name).to eq("田中太郎") end end
before(:each)(デフォルト)は各テストの前に実行されます。before(:all) は全テストの前に1回だけ実行されます。
subject — テスト対象を定義する
RSpec.describe User, type: :model do subject { User.new(name: "田中太郎", email: "tanaka@example.com") } it { is_expected.to be_valid } end
describe にクラスを渡している場合、subject はデフォルトで User.new になります。
モデルスペックの例
バリデーションのテストを中心に書く例です。
# spec/models/user_spec.rb RSpec.describe User, type: :model do describe "バリデーション" do context "name が存在する場合" do let(:user) { build(:user, name: "田中太郎") } it "有効である" do expect(user).to be_valid end end context "name が空の場合" do let(:user) { build(:user, name: "") } it "無効である" do expect(user).not_to be_valid end it "エラーメッセージを含む" do user.valid? expect(user.errors[:name]).to include("can't be blank") end end context "email が重複している場合" do before { create(:user, email: "tanaka@example.com") } let(:user) { build(:user, email: "tanaka@example.com") } it "無効である" do expect(user).not_to be_valid end end end describe "#full_name" do let(:user) { build(:user, first_name: "太郎", last_name: "田中") } it "姓名を返す" do expect(user.full_name).to eq("田中 太郎") end end end
build はDBに保存せずオブジェクトを作成(FactoryBot)、create は保存します。
FactoryBotの基本
# spec/factories/users.rb FactoryBot.define do factory :user do name { "田中太郎" } email { "tanaka@example.com" } end end
user = build(:user) # DB保存なし user = create(:user) # DB保存あり user = build(:user, name: "鈴木花子") # 属性を上書き user = create(:user, email: "suzuki@example.com")
RSpecの実行
# すべてのスペックを実行 bundle exec rspec # 特定のファイルを実行 bundle exec rspec spec/models/user_spec.rb # 特定の行を実行 bundle exec rspec spec/models/user_spec.rb:10 # タグで絞り込む bundle exec rspec --tag focus
失敗したテストだけ再実行する場合:
bundle exec rspec --only-failures
まとめ
describe/context/itでテストの構造を作るexpect(...).to マッチャーで期待値を検証するletでテストデータを定義し、beforeで前処理を書く- FactoryBotを使うとテストデータの作成が楽になる
let と let! の違い(遅延評価・即時評価)は「RSpecのletとlet!:遅延評価と即時評価の違いと使い分け」で詳しく解説しています。
gemのバージョン管理は「Bundler入門:Gemfile・bundle install・bundle execの使い方まとめ」を参照してください。
コードスタイルの自動チェックは「RuboCop導入入門:インストールから設定・git commit時の自動実行まで」を参照してください。
bundle / rake / rails コマンドの早見表は「Rails開発コマンド早見表:bundle / rake / rails の使い分けまとめ」を参照してください。