1
\$\begingroup\$

In my project, I have this kind of code to test my controllers:

#encoding=utf-8
require 'spec_helper'

describe ProjectsController do
  let!(:project) { build_stubbed(:project, user: user) }
  let(:user) { build_stubbed(:user) }

  before(:each) { sign_in user }

  describe "GET 'index'" do
    it "render index template" do
      get 'index'
      should render_template('index')
    end
  end

  describe "GET 'show'" do
    let!(:comment) { build_stubbed(:comment) }

    before(:each) { Project.stubs(:find).returns(project) }

    it "render show template" do
      get 'show', id: project.id
      should render_template('show')
    end

    it "create a new comment" do
      Comment.expects(:new).returns(comment)
      get 'show', id: project.id
    end
  end

  describe "GET 'new'" do
    it "render new template" do
      get 'new'
      should render_template('new')
    end
  end

  describe "POST 'create'" do
    before(:each) do
      Project.stubs(:new).returns(project)
    end

    context "with valid data" do
      before(:each) { project.stubs(:save).returns(true) }

      it "redirect to project's path" do
        post 'create'
        should redirect_to(project)
      end

      it "save the project" do
        lambda do
          post 'create'
        end.should change(user.projects, :size).by(1)
      end

      it "set a flash message" do
        post 'create'
        should set_the_flash[:notice].to("Votre projet a été ajouté")
      end
    end

    context "with invalid data" do
      before(:each) { project.stubs(:save).returns(false) }

      it "render new template" do
        post 'create'
        should render_template('new')
      end
    end
  end

  describe "GET 'edit'" do
    before(:each) do
      Project.stubs(:find).returns(project)
    end

    it "render edit template" do
      get 'edit', id: project.id
      should render_template('edit')
    end
  end

  describe "POST 'update'" do
    before(:each) do
      Project.stubs(:find).returns(project)
    end

    context "when valid" do
      before(:each) { project.stubs(:update_attributes).returns(true) }

      it "redirect to project's path" do
        post 'update', id: project.id
        should redirect_to(project)
      end

      it "update the project" do
        project.expects(:update_attributes)
        post 'update', id: project.id
      end

      it "set a flash message" do
        post 'update', id: project.id
        should set_the_flash[:notice].to("Votre projet a été modifié")
      end
    end

    context "when invalid" do
      before(:each) { project.stubs(:update_attributes).returns(false) }

      it "render edit template" do
        post 'update', id: project.id
        should render_template('edit')
      end
    end
  end

  describe "DELETE 'destroy'" do
    before(:each) do
      Project.stubs(:find).returns(project)
      project.stubs(:destroy)
    end

    it "redirect to project's path" do
      delete 'destroy', id: project.id
      should redirect_to(root_path)
    end

    it "delete the project" do
      project.expects(:destroy)
      delete 'destroy', id: project.id
    end

    it "set a flash message" do
      delete 'destroy', id: project.id
      should set_the_flash[:notice].to("Votre projet a été supprimé")
    end
  end
end

I do this for:

  • Isolate my tests: I don't want to retest the model and I don't want to hit the database. It's faster.
  • Test all actions that should be do by the controller

And I have these questions:

  • Should I test the assignments or is to long for nothing?
  • How should I to stub my models? Is it better to place all stubs in a global before(:each) like this?

    ...
    before(:each) do 
      sign_in user
      Project.stubs(:find).returns(project)
      Project.stubs(:new).returns(project)
      project.stubs(:destroy)
    end
    ...
    

    It's DRYer and cleaner but less efficient.

  • Should I really isolate my tests? I tested it without stubs and I lose 10 seconds. What do you think about that?

\$\endgroup\$
2
\$\begingroup\$

I found this answer helpful; I also like using the shoulda matchers to clean things up.

Yes, I think you should test your assignments. If you use the shoulda matchers, this becomes much less painful, for example:

describe "POST 'update'" do

  context "when invalid" do
    before(:each) do
      project.stubs(:update_attributes).returns(false)
      post 'update', id: project.id
    end

    it { should render_template('edit') }
    it { should assign_to(:project).with(project) }
  end
end

I usually stub where it's relevant rather than at a global level, but I don't have a good argument as to why besides that it's easier for me to understand when I'm reading it.

It's fine to test in isolation as long as you're testing your full stack somewhere like with cucumber or integration tests.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.