RSpec入門:インストールからモデルスペックの書き方まで

スポンサーリンク

RSpec入門:インストールからモデルスペックの書き方まで

はじめに

RSpecはRubyのテストフレームワークで、Railsプロジェクトで最もよく使われています。describecontextitを使って「何をテストするか」を自然言語に近い形で書けるのが特徴です。

この記事では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を使うとテストデータの作成が楽になる

letlet! の違い(遅延評価・即時評価)は「RSpecのletとlet!:遅延評価と即時評価の違いと使い分け」で詳しく解説しています。

gemのバージョン管理は「Bundler入門:Gemfile・bundle install・bundle execの使い方まとめ」を参照してください。

コードスタイルの自動チェックは「RuboCop導入入門:インストールから設定・git commit時の自動実行まで」を参照してください。

bundle / rake / rails コマンドの早見表は「Rails開発コマンド早見表:bundle / rake / rails の使い分けまとめ」を参照してください。