スキップしてメイン コンテンツに移動

RSpecでBDDテスト術

RSpecでBDDテスト術

Ruby応用とRSpecの基礎

Rails アプリケーションを開発する際、テストは欠かせません。RSpec は Ruby 社で最も広く使われているテストフレームワークで、BDD(Behavior Driven Development)を実現するための構文が豊富に用意されています。RSpec の基本構文は describeit で構成され、テスト対象の振る舞いを自然言語で記述できます。

例として、シンプルな Calculator クラスをテストするコードを示します。

RSpec.describe Calculator do
  describe "#add" do
    it "二つの数を足し合わせる" do
      calc = Calculator.new
      expect(calc.add(2, 3)).to eq(5)
    end
  end
end

このように、RSpec はテストケースを読みやすく、保守性の高いコードを書くためのツールです。

should vs expect の違い

RSpec では昔から shouldexpect の二つのマッチャー構文が存在します。should はオブジェクトに対して直接呼び出すスタイルで、expectexpect(object) の形で呼び出します。

近年は should が非推奨となり、expect が推奨されています。理由は以下の通りです。

  • 読みやすさ:expect(...).to の方が自然言語に近い。
  • テストの可読性:expect はメソッドチェーンが明確で、エラー時に原因が追いやすい。
  • 将来の互換性:RSpec の新機能は expect に合わせて実装される。

実際に書き換えると、先ほどの例は次のようになります。

RSpec.describe Calculator do
  describe "#add" do
    it "二つの数を足し合わせる" do
      calc = Calculator.new
      expect(calc.add(2, 3)).to eq(5)
    end
  end
end

このように、expect を使うことでテストコードがより一貫性を持ち、将来の保守が楽になります。

Stub と Mock の使い分け

テストでは外部依存を切り離すために StubMock を使います。両者は似ているようで、目的が異なります。

Stub

Stub は「メソッドの戻り値を固定する」だけで、呼び出し回数や引数の検証は行いません。主に外部サービスやデータベースへのアクセスを簡略化するために使われます。

allow(User).to receive(:find).with(1).and_return(user_stub)

Mock

Mock は「メソッドが呼び出されること自体を検証する」ために使われます。呼び出し回数、引数、戻り値をすべて指定でき、テストの期待値を厳密に定義します。

expect(User).to receive(:find).with(1).and_return(user_stub)

実際のテストでは、外部依存を Stub で切り離し、内部ロジックの呼び出しを Mock で検証するパターンが多いです。

FactoryBot でデータ生成

テストデータを手動で作成するのは手間がかかります。FactoryBot は「ファクトリ」を定義して、必要なオブジェクトを簡単に生成できるツールです。以下は User モデルのファクトリ例です。

FactoryBot.define do
  factory :user do
    name { "テストユーザー" }
    email { "test@example.com" }
    password { "password" }
  end
end

テスト内では次のように呼び出します。

let(:user) { create(:user) }

FactoryBot を使うことで、テストデータの一貫性と再利用性が向上し、テストコードが読みやすくなります。

BDD でテストを書く

Behavior Driven Development(BDD)は「振る舞い」を中心に設計・テストを行う手法です。RSpec は BDD を実現するために設計されており、describecontext を使って「何が起きるか」を自然言語で記述します。

RSpec.describe User, type: :model do
  context "有効な属性を持つとき" do
    it "保存できる" do
      expect(create(:user)).to be_valid
    end
  end

  context "メールアドレスが無いとき" do
    it "無効になる" do
      user = build(:user, email: nil)
      expect(user).not_to be_valid
    end
  end
end

このように、テストは「ユーザーが有効な属性を持つときは保存できる」「メールアドレスが無いときは無効になる」といった振る舞いを明確に表現します。BDD を採用することで、開発者とテスター、さらには非技術者間で共通の理解を得やすくなります。

この記事はAIによって作成されました。

コメント