RSpecでのテスト入門〜よく使う機能 + 見やすいテストを書く編〜


まずはテストの流れを母国語で

  • 対象を決める(機能・処理・画面・メソッド)
  • 条件を決める(〜が〜の時。)※主語を書くこと
  • 条件に対するテストを記述する
  • 期待するアウトプットが返ってくるかを確認する

使い分け(describe/context/example/it)

  • ここで出てきたテストの対象特定の条件アウトプットに注目して、(describe/context/example/it)の使い分けを行う。
  • describe: テストの対象が何かを記述する
    • メソッド名,機能,〜処理,〜画面(E2Eテスト)
  • context: 特定の条件が何かを記述する
    • 奇数・偶数の時,
  • example・it: アウトプットが何かを記述する
    • 日本語で記述するときはexampleを使う。
    • it "is 〜やit { should be 〜 }のような形で書きたい場合はitを使う。

テンプレートとサンプル

  • テンプレート
hoge_spec.rb
describe 'テストの対象' do
  context '特定の条件の内容' do
    example 'アウトプットの内容' do
      ...テスト処理...
    end

    it "is 〜" do ...テスト処理... end

    it { should_be  } 
  end
end
  • サンプル(FizzBazz)
fizzbuzz_spec.rb
describe 'FizzBuzz' do
  describe '#run' do
    context '3の倍数' do
      example 'Fizzという文字列を返すこと' do
        expect(FizzBuzz.run(3)).to eq('Fizz')
        expect(FizzBuzz.run(6)).to eq('Fizz')
      end
    end

    context '5の倍数' do
      example 'Buzzという文字列を返すこと' do
        expect(FizzBuzz.run(5)).to eq('Buzz')
        expect(FizzBuzz.run(10)).to eq('Buzz')
      end
    end

    context '3の倍数かつ5の倍数' do
      example 'FizzBuzzという文字列を返すこと' do
        expect(FizzBuzz.run(15)).to eq('FizzBuzz')
      end
    end

    context '3の倍数ではない かつ 5の倍数ではない' do
      example 'そのままの数字を返すこと' do
        expect(FizzBuzz.run(1)).to eq(1)
        expect(FizzBuzz.run(2)).to eq(2)
      end
    end
  end
end

変数名を見ただけで何をやっているか想像できるようにする

  • 悪い例)question1,question2とか...(マジックナンバーを使用した変数)
  • 良い例)question_without_title(【タイトルが無い質問】と想像がつく)

letでオブジェクトを定義して使用する

letでの共通化とlet!

  • letでは共通化したい定義などをdiscribe直下に書き、使いたいところで使用することが多いようです。
question_spec.rb
describe 'QandA管理機能', type: :system do
  # ユーザA,Bを定義(初めて呼び出された時に生成されるので使用には注意が必要
  let(:user_a) { FactoryBot.create(:user, name: 'ユーザA', email: '[email protected]') }
  let(:user_b) { FactoryBot.create(:user, name: 'ユーザB', email: '[email protected]') }
  # 事前に評価されるのでit内のような使い方ができる
  let!(:question_a) { FactoryBot.create(:question, name: 'name',title: 'test question A',content: 'test Aだよ',author: 'ユーザA',user: user_a) }

  it 'ブログの取得ができること' do
    expect(Blog.first).to eq blog
  end

letは遅延評価・let!は事前評価

  • 先ほどのコードで出てきたlet!の部分ですがこれをletとしてしまうとエラーになります。letの内容は初めて呼び出した時に生成されるためです。もしletで書きたかったらbeforeブロックを使用して書くことができます。

letと同じような機能にsubjectがある

subjectもオブジェクトを定義しておけるという面ではletと似ているが以下のように使う

fuga_spec.rb
# 普通に文字列を定義している例
RSpec.describe User do
  describe '#question' do
    subject { '質問です' }
    context 'question' do
      it '質問が表示されていること' do
        is_expected.to eq '質問です'
      end
    end
  end
end
hoge_spec.rb
# リクエストをsubjectで定義している例
describe "GET 'index'" do
  subject(:action) { get 'index' }
  it { is_expected.to be_success }  # OK
 end
end
  • このように、定義したsubjectを使用する時はis_expectedを使用します。

RSpecの書き方は人それぞれになりがちです。プロジェクトの発足時にルールを策定し徹底することで後から入ってくる人がわかりやすいテストを記述しましょう。