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


前提

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

概要

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

記述方法

正しいビューファイルに繋がるかどうか

render_template

まず、render_templateについて説明します。

ビューファイルに対するテスト

下の例はbooks_controllerのindexアクションにgetメソッドでアクセスした時のテストです
view/gadgets/index.html.erbにアクセスしているかを3種類の方法で記述しています。

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と一致してるか

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

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

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など)の塊です

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にテストの対象を代入します。
  • paramswidget{ :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メソッドを使用した時のクラスを継承しているかどうかを確かめます。

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の時と同様です

終わりに

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