【RSpec】【実践的】使えるテクニック・知識。

# はじめに
自分はRSpecについての理解が全然ない状態で勉強してある程度は使えるようになりました！
初心者なりに実践的な使い方やコツをまとめてみたのでよかったらご覧ください。

## テクニック・知識集

####TDDとは
テスト駆動開発（Test-Driven Development）の略で、テストファーストなプログラムの開発手法のこと。
少しわかりやすく言うと、プログラムの実装前にテストコードを書き、そのテストコードに適合するように実装とリファクタリングを進めていく方法です。

####is_expected
exampleを一行で書くために使う書き方です。

```ruby

it { is_expected.to eq(2) }
```

####use_before_action()
コントローラーで設定したbefore_actionをテスト環境で実行する。

```ruby

it { is_expected.to use_before_action(:set_article) }
```

####respond_with()
レスポンスの種類、状態を返す。

```ruby

is_expected.to respond_with(:redirect)

is_expected.to respond_with 200

is_expected.to respond_with(:success)
```

####redirect_to()
引数に与えたパスにリダイレクトする。

```ruby

is_expected.to redirect_to(new_user_session_path)
```

####set_flash[]
与えた値のフラッシュメッセージが表示されているか確認する。

```ruby

is_expected.to set_flash[:notice]
```

####render_with_layout()
描画されているのが引数の指定したものかを確認する。

```ruby

is_expected.to render_with_layout(:application)
```

####render_template()
引数に指定したアクションのビューが表示されているか確認。

```ruby

is_expected.to render_template(:index)
```

####assigns
コントローラのインスタンス変数をテストするメソッド。

```ruby

# @articlesをテストしている
expect(assigns[:articles].to_sql).to eq Article.order(id: :desc).limit(25).offset(0).to_sql
```

####attributes_for
プロジェクトファクトリからテスト用の属性値をハッシュとして作成します。

```ruby


# 属性のハッシュを生成
alice = attributes_for(:alice)
=> {:name=>"Alice", :admin=>true}
```

####テスト時のHTTPリクエストの仕方

```ruby

get :edit, params: { id: article.id }

post :create, params: { article: article_params }

patch :update, params: { id: article.id, article: edit_article_params }

```

####shared_examples / it_behaves_like
よく使う処理をshared_examplesでまとめて、it_behaves_likeで使うもの。

```ruby

・
・
・
shared_examples '未ログイン時のテスト' do
    it 'result' do
      # response
      is_expected.to respond_with(:redirect)
      is_expected.to redirect_to(new_user_session_path)

      # flash
      is_expected.to_not set_flash[:notice]
    end
  end
・
・
・
    context '未ログイン時' do
      before { get :new }

      it_behaves_like '未ログイン時のテスト'
    end

```

####faxtorybotのアソシエーション方法

```ruby

FactoryBot.define do
  factory :comment do
    association :article
    comment  { 'comment' }
  end
end
```

####validate_presence_of
カラムの存在性をテストするもの。

```ruby

is_expected.to validate_presence_of(:title)
```

####validate_length_of / is_at_most
カラムの長さを調べる。is_at_mostで最大数を調べる。

```ruby

is_expected.to validate_length_of(:title).is_at_most(255)
```

####validate_uniqueness_of
カラムが一意性があるか確認する。

```ruby

is_expected.to validate_presence_of(:username)
```

####ルーティングのテストの書き方

```ruby

it { is_expected.to route(:get, '/').to(controller: 'articles', action: 'index') }
```

####attr_accessor
クラスやモジュールにインスタンス変数を読み書きするためのアクセサメソッドを定義する。
引数には、インスタンス変数名をシンボルか文字列で指定し複数指定できる。
定義されたメソッドは、シンボルの名前の先頭に「@」を付加したインスタンス変数の値を取り出す。
簡単に言うと、外部からインスタンス変数にアクセスが可能になる。

####shared_context / include_context
context を再利用するためのもの。

```ruby
・
・
・
  shared_context '12歳の場合' do
    let(:age) { 12 }
  end
・
・
・
    context '12歳以下の場合' do
      include_context '12歳の場合'
      it { is_expected.to eq 'ぼくはU-12だよ。' }
    end
```

####let!
通常のletだと、遅延評価されるが「!」をつけるとexampleの前に実行してくれるようになる。

####pending
なぜかどうしてもテストがパスしないといったテストを、一旦保留にしたいときに使います。

```ruby

RSpec.describe '謎クラス' do
  it '謎テスト' do
    expect(1 + 2).to eq 3

    pending 'この先はなぜかテストが失敗するので保留'
    # パスしないエクスペクテーション（実行される）
    expect(foo).to eq bar
  end
end
```

####skip
指定した場所でテストの実行を止めたい場合に使います。

```ruby

RSpec.describe '実行したくないクラス' do
  it '実行したくないテスト' do
    expect(1 + 2).to eq 3

    skip 'とりあえずここで実行を止める'
    # ここから先は実行されない
    expect(foo).to eq bar
  end
end
```

####xit
example全体を手っ取り早くskipさせたいときは it を xit に変更するとそのexampleは実行されなくなります。（pending 扱い）

```ruby

RSpec.describe '何らかの理由で実行したくないクラス' do
  xit '実行したくないテスト' do
    expect(1 + 2).to eq 3

    expect(foo).to eq bar
  end
end
```

####xdescribe / xcontext
describe や context にも x を付けることでスキップできる。

```ruby

# グループ全体をskipする
xdescribe '四則演算' do
  it '1 + 1 は 2 になること' do
    expect(1 + 1).to eq 2
  end
  it '10 - 1 は 9 になること' do
    expect(10 - 1).to eq 9
  end
end

# グループ全体をskipする
xcontext '管理者の場合' do
  it '社員情報を編集できる' do
    # ...
  end
  it '社員情報を削除できる' do
    # ...
  end
end
```

##よく使うマッチャ
マッチャ（matcher）は「期待値と実際の値を比較して、一致した（もしくは一致しなかった）という結果を返すオブジェクト」のことです。
以降は良く使うマッチャをいくつか紹介します。

####eq
期待値と実際の値が「等しい」かどうかを検証する

```ruby

expect(1 + 2).to eq 3
```

####be
等号・不等号と組み合わせて、値の大小を検証する

```ruby

expect(1 + 2).to be >= 3
```

####be_xxx (predicateマッチャ)
戻り値が true / false になるメソッドを検証できる

```ruby

expect([]).to be_empty?

expect(article).to be_valid?
```

####be_truthy / be_falsey
「trueっぽい値」または「falseっぽい値」かどうかを検証する。

```ruby

# 必須項目が入力されていないので保存できない（結果はfalse）
user = User.new
expect(user.save).to be_falsey 

# 必須項目が入力されているので保存できる（結果はtrue）
user.name = 'Tanaka'
user.email = 'tanaka@example.com'
expect(user.save).to be_truthy
```

####change + from / to / by
X すると Y が A から B に変わることを期待することを検証します。

```ruby

x = [1, 2, 3, 4, 5]
expect{ x.pop }.to change{ x.size }.from(5).to(4)
```

####include
～が含まれていることを検証する。

```ruby

x = [1, 2, 3]
expect(x).to include 1
```

####raise_error
「エラーが起きること」を検証する。

```ruby

it 'nilを追加するとエラーが発生すること' do
  cart = Cart.new
  expect{ cart.add nil }.to raise_error 'Item is nil.'
end
```

####be_within + of
数値 X がプラスマイナス Y の範囲内に収まっていることを検証する。

```ruby

it '当選確率が約25%になっていること' do
  results = Lottery.generate_results(10000)
  win_count = results.count(&:win?)
  probability = win_count.to_f / 10000 * 100

  expect(probability).to be_within(1.0).of(25.0)
end
```

##RSpecマナーの教え
其の１ : describe の引数にはテストの対象を書く

其の２ : context の引数にはテストが実行される際に前提になる条件や状態を書く

其の３ : describe 外にテストデータを置かない

其の４ : letを上書きしない

其の５ : DRYよりも読みやすさ重視！

##終わりに
ここまでRSpecに役立つ知識やテクニックを詰め込んできましたが、実際に作業しているとよくわからないことばかり出てきますが、分からないことは誰かに調べたり、誰かに聞いたりしてとにかく経験値を積み上げていくのがテストマスターへの近道では無いかと思います！
自分も頑張ろう。。