views:

52

answers:

2

Here's my code I'm spec'ing:

def vote_up
  get_vote
  @vote.value += 1 unless @vote.value == 1
  @vote.save
  respond_to do |format|
    format.js { render :action => "vote", :layout => false }
  end
end

Seems pretty straightforward. This is what I'm trying to spec it with :

  it "should vote up" do
    @mock_cat = Factory.create(:category)
    Category.stub(:mock_cat)
    @mock_post = Factory.create(:post)
    Post.stub(:current_post).and_return(@mock_post)

    @vote = Factory(:vote)

    get :vote_up, :id => @vote
    @vote.reload.value.should == 1    
  end

It's returning this :

undefined method `to_i' for #<Vote:0x1052a4af8>

I can't really figure out why though. If I stubbed my mock_vote as (:vote), wouldn't it run through the controller method and get +1 attributed to it?

Update

Here's the private method from my posts_controller.rb

private

def get_vote
  current_post = Post.all.detect{|r| r.id == params[:id].to_i}
  @post = current_post
  @vote = current_post.votes.find_by_user_id(current_user.id)
  unless @vote
    @vote = Vote.create(:user_id => current_user.id, :value => 0)
    current_post.votes << @vote
  end
end

Answer:

  it "should vote up" do
    @mock_cat = Factory.create(:category)
    Category.stub(:mock_cat)
    @post = Factory(:post)

    get :vote_up, :id => @post.id
    @post.reload.vote_score.should == 1    
  end
+1  A: 

It's hard to follow what exactly your stubs are doing because you didn't post the code for get_vote. But I think you're over-using stubs when you could just be taking advantage of the factories you're already creating.

it "should vote up" do
  # Does your Vote belong to a post or a category or anything? I don't know. 
  # Modify as needed -- Factory(:vote, :post => Factory(:post))
  @vote = Factory(:vote) 

  get :vote_up, :id => @vote
  @vote.reload.value.should == 1
end

Note the reload. Your controller's modifying a record we've already pulled from the database, so we need to reload it to check its new value.

rspeicher
Great point. I think I should enter a 12 step stub program for my addiction :) . I tried your code with mine and posted it above, and it returned an `undefined method `to_i' for #<Vote:0x1052a4af8>` error. Probably just like you said, its because of my get_vote method.
Trip
I was going to update my answer with how to do this, but I feel like I'm getting into the territory where I'm just writing your app for you. Debugging that error is pretty simple - where are you calling `to_i`? In your `Post.all.detect` block. Why's it failing? In your spec you passed `:id => @vote`, where @vote is a Vote **object** that doesn't have a `to_i` method. So change it to `:id => @vote.id`, or just use `Post.find(params[:id])` which automatically calls `to_param`, which by default is the object's `id` attribute.
rspeicher
@rspeicher, ah, you are truely wise. You have taught a man to fish, truely. I just completed it, and updated my answer above.
Trip
+1  A: 

I recommend to move logic from controller to model. Models are much easier to spec. Instead of:

def vote_up
  get_vote
  @vote.value += 1 unless @vote.value == 1
  @vote.save
  respond_to do |format|
    format.js { render :action => "vote", :layout => false }
  end
end

Your controller should looks like this:

def vote_up
  @vote = Vote.vote_up(params[:id])
  respond_to do |format|
    format.js { render :action => "vote", :layout => false }
  end
end

or, if you really need get_vote (may be you should move it to before_filter?)

def vote_up
  @vote.vote_up
  respond_to do |format|
    format.js { render :action => "vote", :layout => false }
  end
end

If necessary. add exceptions (may be with rescue_from)

Then, you will need specs only in model, and some integration (rspec,steac, cucumber)

Sławosz
Ah great recommendation. Never thought of doing that before.
Trip