Testing Controller with Rspec

Rspec is awesome when it comes to testing. In this post I am going to post code for simple controller and than I am going to post some code to test this controller. Both code and test can be refactored further but I am not refactoring them at this moment so that they will be easier to read. Once our test will pass then we will try to refactor it.

First of all, here is my code of controller which have seven default actions.


class PostsController < ApplicationController
  def index
    @posts = Post.all
  end

  def show
    @post = Post.find(params[:id])
  end

  def new
    @post = Post.new
  end

  def create
    post = Post.new(params[:post])
    if post.save
      flash[:notice] = "Post created sucessfully."
      redirect_to root_path
    else
      flash[:error] = "Post creation failed."
      render :new
    end
  end

  def edit
    @post = Post.find(params[:id])
  end

  def update
    post = Post.find(params[:id])
    if post.update_attributes(params[:post])
      flash[:notice] = "Post upaded sucessfully."
      redirect_to root_path
    else
      flash[:error] = "Post update failed."
      render :edit
    end
  end

  def destroy
    Post.find(params[:id]).destroy
    flash[:notice] = "Post deleted sucessfully."
    redirect_to root_path
  end
end

Now below in my test for this controller. While testing controller we should focus mainly on three things:
1. We should check if instance variable has been set properly for view or not?
2. We should check what template response is going to render or which action its going to redirect.
3. Finally, If flash notice and flash errors has been properly set or not.

We don’t need to test anything more than this into controller. We should not test if our create method is going to increase database count or not. These type of tests doesn’t belong in controller test.

require 'spec_helper'

describe PostsController do
  let!(:first_post)  { Post.create(:title => "Test Title", :body => "Test Body")}

  describe "GET 'index'" do
    before { get :index }

    it "assigns @posts" do
      expect(assigns(:posts)).to eq([first_post])
    end

    it "renders the index template" do
      expect(response).to render_template("index")
    end
  end

  describe "GET 'show'" do
    before { get :show , :id => first_post.id }

    it "assigns @post" do
      expect(assigns(:post)).to eq(first_post)
    end

    it "renders the show template" do
      expect(response).to render_template("show")
    end
  end

  describe "GET 'new'" do
    before { get :new }

    it "assigns @post" do
      expect(assigns(:post)).to be_a_new(Post)
    end

    it "renders the new template" do
      expect(response).to render_template("new")
    end
  end

  describe "POST 'create'" do
    context "when valid" do
      before { post :create, :post => {:title => "Test Title", :body => "Test Body"} }

      it "will redirect to root path" do
        expect(response).to redirect_to(root_path)
      end

      it "will set flash[:notice]" do
        expect(flash[:notice]).to be_present
      end
    end

    context "when invalid" do
      before { post :create, :post => {:title => "Test Title", :body => ""} }

      it "will render new template" do
        expect(response).to render_template("new")
      end

      it "will set flash[:error]" do
        expect(flash[:error]).to be_present
      end
    end
  end

  describe "GET 'edit'" do
    before { get :edit, :id => first_post.id }

    it "assigns @post" do
      expect(assigns(:post)).to eq(first_post)
    end

    it "renders the edit template" do
      expect(response).to render_template("edit")
    end
  end

  describe "PUT 'update'" do
    context "when success" do
      before { put :update, :post => {:title => "Update Title", :body => "Update Body"},:id => first_post.id }

      it "will redirect to root path" do
        expect(response).to redirect_to root_path
      end

      it "will set flash[:notice]" do
        expect(flash[:notice]).to be_present
      end
    end

    context "when not success" do
      before { put :update, :post => {:title => "", :body => ""},:id => first_post.id }

      it "will render new template" do
        expect(response).to render_template("edit")
      end

      it "will set flash[:error]" do
        expect(flash[:error]).to be_present
      end
    end
  end

  describe "DELETE 'destroy'" do
    before { delete :update, :id => first_post.id }

    it " will redirect to posts path" do
      expect(response).to redirect_to root_path
    end

    it "will set flash[:notice]" do
      expect(flash[:notice]).to be_present
    end
  end
end

Refactored Version of Rspec

Below is refactored version of Rspec, its smaller in size than previous test.

require 'spec_helper'

describe PostsController do
  let!(:first_post)  { Post.create(:title => "Test Title", :body => "Test Body")}

  describe "GET 'index'" do
    before { get :index }

    it { expect(assigns(:posts)).to eq([first_post]) }
    it { expect(response).to render_template("index") }
  end

  describe "GET 'show'" do
    before { get :show , :id => first_post.id }

    it { expect(assigns(:post)).to eq(first_post) }
    it { expect(response).to render_template("show") }
  end

  describe "GET 'new'" do
    before { get :new }

    it { expect(assigns(:post)).to be_a_new(Post) }
    it { expect(response).to render_template("new") }
  end

  describe "POST 'create'" do
    context "when valid" do
      before { post :create, :post => {:title => "Test Title", :body => "Test Body"} }

      it { expect(response).to redirect_to(root_path) }
      it { expect(flash[:notice]).to be_present }
    end

    context "when invalid" do
      before { post :create, :post => {:title => "Test Title", :body => ""} }

      it { expect(response).to render_template("new") }
      it { expect(flash[:error]).to be_present }
    end
  end

  describe "GET 'edit'" do
    before { get :edit, :id => first_post.id }

    it { expect(assigns(:post)).to eq(first_post) }
    it { expect(response).to render_template("edit") }
  end

  describe "PUT 'update'" do
    context "when success" do
      before { put :update, :post => {:title => "Update Title", :body => "Update Body"},:id => first_post.id }

      it { expect(response).to redirect_to root_path }
      it { expect(flash[:notice]).to be_present }
    end

    context "when not success" do
      before { put :update, :post => {:title => "", :body => ""},:id => first_post.id }

      it { expect(response).to render_template("edit") }
      it { expect(flash[:error]).to be_present }
    end
  end

  describe "DELETE 'destroy'" do
    before { delete :update, :id => first_post.id }

    it { expect(response).to redirect_to root_path }
    it { expect(flash[:notice]).to be_present }
  end
end

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s