开发者

RSpec Newbie: "Update attributes => false" not being recognised

Just starting out with RSpec. Everything is going smoothly, except for one spec with nested controllers.

I'm trying to ensure that when a 'comment' resource (nested under 'post') is updated with invalid parameters, it renders the 'edit' template. I'm struggling to get rspec to recognise the :update_attributes => false trigger. If anyone has any suggestions, they'd be very appreciated. Attempted code below:

  def mock_comment(stubs={})
    stubs[:post] = return_post
    stubs[:user] = return_user
    @mock_comment ||= mock_model(Comment, stubs).as_null_object
  end

  describe "with invalid paramters" dog
    it "re-renders the 'edit' template" do
      Comment.stub(:find).with("12") { mock_comment(:update_attributes => false) }
    开发者_运维技巧  put :update, :post_id => mock_comment.post.id, :id => "12"
      response.should render_template("edit")
    end
  end

And the controller:

  def update
    @comment = Comment.find(params[:id])
    respond_to do |format|
      if @comment.update_attributes(params[:comment])
        flash[:notice] = 'Post successfully updated'
        format.html { redirect_to(@comment.post) }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @comment.errors, :status => :unprocessable_entity }
      end
    end

  end

And finally, the error:

 Failure/Error: response.should render_template("edit")
   expecting <"edit"> but rendering with <"">.
   Expected block to return true value.


This is quite an interesting problem. A quick fix is to simply replace the block form of Comment.stub:

Comment.stub(:find).with("12") { mock_comment(:update_attributes => false) }

with an explicit and_return:

Comment.stub(:find).with("12").\
  and_return(mock_comment(:update_attributes => false))

As to why these two forms should produce different results, that's a bit of a head-scratcher. If you play around with the first form you'll see that the mock is actually returning self instead of false when the stubbed method is called. That's tells us it hasn't stubbed the method (since it's specified as a null object).

The answer is that when passing in a block, the block is only executed when the stubbed method is called, not when the stub is defined. So when using the block form, the following call:

put :update, :post_id => mock_comment.post.id, :id => "12"

is executing mock_comment for the first time. Since :update_attributes => false is not being passed in, the method is not stubbed, and the mock is returned rather than false. When the block invokes mock_comment it returns @mock_comment, which doesn't have the stub.

Contrariwise, using the explicit form of and_return invokes mock_comment immediately. It would probably be better to use the instance variable instead of calling the method each time to make the intent clearer:

it "re-renders the 'edit' template" do
  mock_comment(:update_attributes => false)
  Comment.stub(:find).with("12") { @mock_comment }
  put :update, :post_id => @mock_comment.post.id, :id => "12"
  response.should render_template("edit")
end
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜