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

- 前回は基本の基本を書きました。[Rspecでのテスト入門~E2E(System)テスト基本文法編~](https://qiita.com/sukezane/items/915fc3092ee88bed3ff2)
- 今回は一段上がって基本的な機能をアウトプット！！

# まずはテストの流れを母国語で
- 対象を決める（機能・処理・画面・メソッド）
- 条件を決める（〜が〜の時。）※主語を書くこと
- 条件に対するテストを記述する
- 期待するアウトプットが返ってくるかを確認する

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

# テンプレートとサンプル
- テンプレート

```ruby:hoge_spec.rb
describe 'テストの対象' do
  context '特定の条件の内容' do
    example 'アウトプットの内容' do
      ...テスト処理...
    end

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

    it { should_be 〜 } 
  end
end
```

- サンプル（FizzBazz)

```ruby: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`<font color="red">(【タイトルが無い質問】と想像がつく)</font>

# `let`でオブジェクトを定義して使用する
- 以下がわかりやすかったです。
- [RSpecのletを使うのはどんなときか？（翻訳）](https://qiita.com/jnchito/items/cdd9eef2ed193267c651)

# `let`での共通化と`let!`
- `let`では共通化したい定義などを`discribe`直下に書き、使いたいところで使用することが多いようです。

```ruby:question_spec.rb
describe 'QandA管理機能', type: :system do
  # ユーザA,Bを定義（初めて呼び出された時に生成されるので使用には注意が必要
  let(:user_a) { FactoryBot.create(:user, name: 'ユーザA', email: 'a0@email.com') }
  let(:user_b) { FactoryBot.create(:user, name: 'ユーザB', email: 'b0@email.com') }
  # 事前に評価されるので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の内容は<font color="OrangeRed">初めて呼び出した時に生成される</font>ためです。もし`let`で書きたかったら`before`ブロックを使用して書くことができます。

# `let`と同じような機能に`subject`がある
subjectもオブジェクトを定義しておけるという面ではletと似ているが以下のように使う

```ruby:fuga_spec.rb
# 普通に文字列を定義している例
RSpec.describe User do
  describe '#question' do
    subject { '質問です' }
    context 'question' do
      it '質問が表示されていること' do
        is_expected.to eq '質問です'
      end
    end
  end
end
```

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