开发者

How to post to create with spec helper method in controller spec?

开发者 https://www.devze.com 2023-03-04 04:05 出处:网络
I\'m relatively new to programming, Rails, Ruby, Rspec, and the like, so thanks for your help! My specs were very repetitive, so I wrote some spec helper methods.I can\'t figure out how to properly u

I'm relatively new to programming, Rails, Ruby, Rspec, and the like, so thanks for your help!

My specs were very repetitive, so I wrote some spec helper methods. I can't figure out how to properly use them in my specs. Specifically, I have a users controller with create:

  def create
    @user = User.new(params[:user])
    if @user.save
      redirect_to user_path(@user)
    else
      render :action => :new
    end
  end

A bit in the spec helper that creates a valid user:

def valid_user_eilif
  @test_image = Rails.root + "spec/fixtures/images/seagull.jpg"
  @file = Rack::Test::UploadedFile.new(@test_image, "image/jpeg")
  user = User.create!(:username => "eilif", :email => "eilif@email.org",
  :image => @file, :bio => "Lots of text that I don't want to write",
  :signature_quote => "Yet more text.")
  user.save!
  user
end

And then in my user controller spec:

before (:each) do
  post :create, :user => valid_user_eilif
end

it 'should assign user to @user' do
  assigns(:user).should eq(User.last)
end

When I run the spec I get the error:

 Failure/Error: assigns(:user).should eq(User.last)

   expected #<User id: 1, username: "eilif", email: "eilif@email.org", bio: "Lots of text that I don't want to write", signature_quote: "I feel empty.", image_file_name: "seagull.jpg", imag开发者_Python百科e_content_type: "image/jpeg", image_file_size: 10475, image_updated_at: "2011-05-10 23:35:55", created_at: "2011-05-10 23:35:56", updated_at: "2011-05-10 23:35:56">
        got #<User id: nil, username: nil, email: nil, bio: nil, signature_quote: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, created_at: nil, updated_at: nil>

So, I assume I'm incorrectly posting to create, since nothing is created? What's the proper way to do this?


Ideally controller specs shouldn't depend on the model being able to create a row in the database. With such a simple action you can mock out the dependencies:

describe UsersController do
  context "on success" do
    before(:each) do
      @user = mock_model(User,:save=>true)
      User.stub(:new) {@user}
      post :create, :user => {}
    end

    it "redirects" do
      response.should redirect_to(user_path(@user))
    end

    it "assigns" do
      assigns[:user].should == @user
    end
  end
  context "on failure" do
    it "renders 'new'" do
      @user = mock_model(User,:save=>false)
      User.stub(:new) {@user}
      post :create, :user => {}
      response.should render_template "users/new"
    end
  end
end

Notice that the specs don't pass anything in params[:user]. This helps enforce the MVC separation of concerns, whereby the model is responsible for handling the attributes, ie. validating, setting up associations, etc. You can't always keep controllers this 'skinny', but it's a good idea to try.


It looks like the problem is that @user doesn't get refreshed after the save. Try assigns(:user).reload.should eql(User.last).

But there's another slight problem, and that's probably still going to fail. You shouldn't be calling post with :user => valid_user_eilif; you want the attributes from your user record, not the actual user object itself. And you're essentially creating a new user in valid_user_eilif and then making your controller create that object again -- if you have any kind of unique constraints, you're going to get a conflict.

This is a good place to use something like factory_girl and mocks. For an example, take a look at how one of my projects handles controller specs. This example uses factory_girl, Mocha and shoulda. I'll annotate it with comments below:

describe MembersController, "POST create" do
  before do
    # Factory Girl - builds a record but doesn't save it
    @resource = Factory.build(:member)

    # Mocha expectation - overrides the default "new" behavior and makes it 
    # return our resource from above
    Member.expects(:new).with({}).returns(@resource)

    # Note how we expect it to be called with an empty hash; that's from the
    # `:member` parameter to `post` below.
  end

  context "success" do
    before do
      post :create, :member => {}
    end

    # shoulda matchers - check for a flash message and a redirect
    it { should set_the_flash.to(/successfully created/) }
    it { should redirect_to(member_path(@resource)) }
  end

  context "failure" do
    before do
      # Mocha - To test a failing example in the controller, we override the
      # default `save` behavior and make it return false, otherwise it would
      # be true
      @resource.expects(:save).returns(false)
      post :create, :member => {}
    end

    # shoulda matchers - check for no flash message and re-render the form
    it { should_not set_the_flash }
    it { should render_template(:new) }
  end
end
0

精彩评论

暂无评论...
验证码 换一张
取 消