【Rails】RSpecでコントローラーのテストをする

#前提
現在、転職のためのポートフォリオを作成しています。
Ruby on Railsはある程度なら書けるようになりましたが、
RSpecのテストの文法は意味がわからん！！
検索しまくってもあんまりいい感じの記事に巡り合わなかったので、
ある程度まとめておこうと思います！！
今回はRSpecでコントローラーのテストについて解説していきます。

#概要
今回、説明するテストの内容は以下の２点です。
正しいビューファイルに繋がるかどうか（render_template、redirect_to）
作成、保存されたインスタンス変数の型が同じかどうか（be_a_new）
詳しい使い方を説明します。


#記述方法

##正しいビューファイルに繋がるかどうか
###render_template
まず、render_templateについて説明します。
####ビューファイルに対するテスト
下の例はbooks_controllerのindexアクションにgetメソッドでアクセスした時のテストです
view/gadgets/index.html.erbにアクセスしているかを３種類の方法で記述しています。

```ruby:spec/controllers/gadgets_controller_spec.rb
describe "GET #index" do
    subject { get :index }

    it "renders the index template" do
      expect(subject).to render_template(:index)
      expect(subject).to render_template("index")
      expect(subject).to render_template("gadgets/index")
    end

    it "does not render a different template" do
      expect(subject).to_not render_template("gadgets/show")
    end
  end
```

`subject`にテストの対象になるものを入れます。
`subject`はgetメソッドでindexにアクセスした時のパラメータを表ています
`subject`のアクセスしてるURLがrender_template()した時のURLと一致してるか

####部分テンプレートに対するテスト

正しい部分テンプレートが呼び出されているかテストします。

```ruby:spec/controllers/gadgets_controller_spec.rb
describe "GET #index" do
    subject { get :index }

    it "renders the application layout" do
      expect(subject).to render_template("layouts/application")
    end

    it "does not render a different layout" do
      expect(subject).to_not render_template("layouts/admin")
    end
  end
```

* `expect(subject).to render_template("layouts/application")`

 * `subject`の部分テンプレートのパラメータが`layouts/application`と一致して**いる**か

* `expect(subject).to_not render_template("layouts/admin")`
  * `subject`の部分テンプレートのパラメータが`layouts/admin`と一致して**いない**か


###redirect_to
redirect_toについて説明します。

以下の例がpostメソッドでcreateにparamsをもってアクセスした時のテストです。
`params`はビューから送られるもの（フォームで入力された文字、URLなど）の塊です

```ruby:spec/controllers/gadgets_controller_spec.rb
describe "#create" do
    subject { post :create, :params => { :widget => { :name => "Foo" } } }

    it "redirects to widget_url(@widget)" do
      expect(subject).to redirect_to(widget_url(assigns(:widget)))
    end

    it "redirects_to :action => :show" do
      expect(subject).to redirect_to :action => :show,
                                     :id => assigns(:widget).id
    end

    it "redirects_to /widgets/:id" do
      expect(subject).to redirect_to("/widgets/#{assigns(:widget).id}")
    end
  end
```

`subject { post :create, :params => { :widget => { :name => "Foo" } } }`

* `subject`にテストの対象を代入します。
* `params`の`widget`に`{ :name => "Foo" }`を代入した状態でアクセスします。


`expect(subject).to redirect_to(widget_url(assigns(:widget)))`

* `widget_url(assigns(:widget))`に`redirect_to`した時、`subject`と一致するか
* `assigns(:widget)`:コントローラー内で作成した@widgetです
* assignsメソッドを使用するとコントローラー内で使用したインスタンス変数（@postなど@のついた変数）を参照できます
* `widget_url()`はwidgetの詳細のリンクです。（widget_path()でもOKです）

##作成、保存されたインスタンス変数の型が同じかどうか
new、createメソッドを使用した時のクラスを継承しているかどうかを確かめます。

```ruby:spec/controllers/gadgets_controller_spec.rb
context "when initialized" do
    subject(:widget) { Widget.new }

    it "is a new widget" do
      expect(widget).to be_a_new(Widget)
    end

    it "is not a new string" do
      expect(widget).not_to be_a_new(String)
    end
  end
```

`expect(widget).to be_a_new(Widget)`

* 作成された`widget`がWidgetモデルの型と一致して**いる**かどうか

`expect(widget).not_to be_a_new(String)`

* 作成された`widget`がString型(文字列)と一致して**いない**かどうか

```
  context "when saved" do
    subject(:widget) { Widget.create }

    it "is not a new widget" do
      expect(widget).not_to be_a_new(Widget)
    end

    it "is not a new string" do
      expect(widget).not_to be_a_new(String)
    end
  end
```

`new`の時と同様です

#終わりに

**疑問、気になるところございましたら質問、コメントよろしくお願いいたします！！**