RSpec で subject & expect する時の注意点

どの段階でテストコードが評価されているか注意する。

 ```expect``` と ```is_expected``` 何が違うの？？な人のための記事です。

### ```subject``` を使うのはどういうとき？

定義元のコメントを見てみると・・

```rspec/core/memoized_helpers.rb
# Declares a `subject` for an example group which can then be wrapped
# with `expect` using `is_expected` to make it the target of an
# expectation in a concise, one-line example.
# ...
def subject(name=nil, &block)
  if name
    let(name, &block)
    alias_method :subject, name

    self::NamedSubjectPreventSuper.__send__(:define_method, name) do
      raise NotImplementedError, "`super` in named subjects is not supported"
    end
  else
    let(:subject, &block)
  end
end
```

↑```is_expected``` を使って簡潔な一行 example を書くため、らしい。
なのでsubject と is_expected はペアで使う。

```subject``` の実態は要するに ``` let ``` （※letがわからない場合は[ここ](http://qiita.com/jnchito/items/42193d066bd61c740612#%E9%81%85%E5%BB%B6%E8%A9%95%E4%BE%A1%E3%81%95%E3%82%8C%E3%82%8B-let-%E3%81%A8%E4%BA%8B%E5%89%8D%E3%81%AB%E5%AE%9F%E8%A1%8C%E3%81%95%E3%82%8C%E3%82%8B-let)が詳しい解説）

そして ```let!``` ではないので、この時点ではまだ評価されてない。

### ```is_expected``` は何なのか？

```spec/controllers/books_controller_spec.rb
# Wraps the `subject` in `expect` to make it the target of an expectation.
# ...
# Designed to read nicely for one-liners.
def is_expected
  expect(subject)
end
```

インスタンス変数の ```subject``` を呼び出してる。 
なのでこいつをペアで呼んであげると、subjectに渡したブロックが評価される。

### 特にハマリポイントはない王道パターンの使い方

```spec/controllers/books_controller_spec.rb
RSpec.describe BooksController do
 
 describe "GET 'index'" do
  subject(:action) { get 'index' }
  it { is_expected.to be_success }  # OK
 end
end
```


### ```expect``` を使いたい時は？

is_expected じゃなくて expect だと何が違う？

```spec/controllers/books_controller_spec.rb

 # さっきのテストコードの続き

 let(:param) { {title: 'My Book'} } # 適当にcreateが成立するパラメータが定義されてるとする

 # ステータスコードチェックしてみる
 describe "POST 'create'" do
  # json のリクエストみたいに成功したら 201 (:craeted)が戻るコントローラーを例に
  subject(:action) { post 'create', params }
  it { expect(subject.status).to eq 201 }            # 1.OK
  it { expect(action.status).to eq 201 }             # 2.OK
  it { expect(response.status).to eq 201 }           # 3.NG
 end
```

#### それぞれ何をやってるか

##### 1. ```expect(subject.status).to eq 201```

subject -> let(:subject)で定義されたブロック（上の例で ```{ post 'create' }```のとこ）を評価して、 ```subject.status``` をチェック

##### 2. ```expect(action.status).to eq 201```

subject の name として ```action``` が渡ってるので、 ここでは ```subject == action```
1と同じ。

##### 3. ```expect(response.status).to eq 201```

subject を評価していないので、 インスタンス変数 response の値は初期値のまま変わってない。
のでエラー
（status==200が初期値なので、GET#index の所で 200 チェックしてると、この現象に気づきづらい）

### 通したい場合

#### ```expect(subject)``` で評価

```
 describe "POST 'create'" do
  subject(:action) { post 'create', params }
  it do
    expect(subject) # ここで評価
    expect(response.status).to eq 201
  end
 end
```

#### ```subject``` だけでも良い

```
 describe "POST 'create'" do
  subject(:action) { post 'create', params }
  it do
    subject # ここで評価
    expect(response.status).to eq 201
  end
 end
```

#### ```subject！```

let, let!の関係から推測・・

```
 describe "POST 'create'" do
  subject!(:action) { post 'create', params }
  it do
    expect(response.status).to eq 201
  end
 end
```

でもOK :)

- - -

#### statusチェックしたいだけなら ```is_expected``` でもできる

```
  it { is_expected.to have_http_status(201) }        # OK
  it { is_expected.to have_attributes(status: 201) } # OK
```

・・のでこんなとこでハマる人居ないはず；；


#### assigns チェックの時はこんな感じ

```
  let!(:books) { FactoryGirl.create_list(:book, 10) }
  subject(:action) { get 'index' }
  it do
    expect(subject)
    expect(assigns(:books)).to eq(books)
  end
```

```
  let!(:books) { FactoryGirl.create_list(:book, 10) }
  subject(:action) { get 'index' }
  it do
    subject
    expect(assigns(:books)).to eq(books)
  end
```

```
  let!(:books) { FactoryGirl.create_list(:book, 10) }
  subject!(:action) { get 'index' }
  it do
    expect(assigns(:books)).to eq(books)
  end
```

上の「通したい場合」のどのパターンでも良い。