RSpec test PUT update action

好久不见. 提交于 2019-12-23 13:22:44

问题


I am trying to write some RSpec tests to test my app, but I've stumbled on several problems that I can't find any solution. 1) I am trying to test the update action. Here is my code:

        it "email is a new one" do
            put :update, id: @user, user: FactoryGirl.attributes_for(:user, :email=>"a@b.c")
            @user.reload
            @user.email.should == "a@b.c"
            puts @user.email
        end

Here is the UsersController update action:

  def update
    @user = User.find(params[:id])
    respond_to do |format|
      if @user.update_attributes(params[:user])
        format.html { redirect_to edit_user_path(@user), :notice => "Your settings were successfully updated."}
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

Here is the error:

 Failure/Error: @user.email.should == "a@b.c"
       expected: "a@b.c"
            got: "user16@example.com" (using ==)

It's obvious that the test hasn't changed the email of the user. I took the update action tutorial from here: http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html . Where I could find the solution?


回答1:


Could @user.update_attributes(params[:user]) be failing for a validation reason?

Also, you could ensure that your test and the controller method are interacting with the same ruby object. That's been a gotcha for me in the past. The way I do it is to stub the find method on the class.

it "email is a new one" do
  User.stubs(:find).returns(@user)
  put :update, id: @user, user: FactoryGirl.attributes_for(:user, :email=>"a@b.c")
  @user.reload
  @user.email.should == "a@b.c"
  puts @user.email
end

This ensures that you're talking about not only the same record, but the same object during the test.


Lastly, I would argue that your test is doing very much for you. You're basically testing update_attributes, which is a core feature and thoroughly tested already. I would focus on testing the controller behavior. Something like this:

let(:user) { FactoryGirl.create(:user) }

describe "PUT #update" do

  before(:each) {
    User.stubs(:find).returns(user)
  }

  it "should redirect to the user path on succesful save" do
    user.should_receive(:update_attributes).and_return true
    put :update, user, {}
    response.should redirect_to(edit_user_path(user))
  end

  it "should render the edit screen again with errors if the model doesn't save" do
    user.should_receive(:update_attributes).and_return false
    put :update, user, {}
    response.should render_template("edit")
  end
end



回答2:


I think the argument for put is incorrect.

put, get, delete, post accept three arguments. The first is path, second is params, and the third is options.

In you code you put two params as two arguments, it's incorrect.

put :update, id: @user, user: FactoryGirl.attributes_for(:user, :email=>"a@b.c")

So, change it to

put :update, {id: @user, user: FactoryGirl.attributes_for(:user, :email=>"a@b.c")}

But, wait! your code will work by above change, but there is security hole in your current code. Make sure you'll add authority check in controller code. For example

return unauthorized unless @user == current_user || current_user.role == "admin"


来源:https://stackoverflow.com/questions/17389720/rspec-test-put-update-action

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!