I just spent about an hour trying to figure out why none of my RESTful routes were working properly within my RSpec controller specs. In my controller I had some boilerplate code like this:
# POST /categories
# POST /categories.xml
def create
@category = Category.new(params[:category])
respond_to do |format|
if @category.save
flash[:notice] = 'Category was successfully created.'
format.html { redirect_to category_url(@category) }
format.xml { head :created, :location => category_url(@category) }
else
format.html { render :action => "new" }
format.xml { render :xml => @category.errors.to_xml }
end
end
end
The line causing the problem was:
redirect_to category_url(@category)
I kept receiving an error on the eval of category_url
with an error description of “can’t convert Fixnum into String”.
I tried replacing @category with @category.id to see if I would get different results. The error went away but the test failed indicating that the id returned from the @category instance was not the same as I was expecting. This led me to determine that I needed to stub out the id property on my class. So I added the following to my setup:
@category.stub!(:id).and_return(1)
Everything worked. Problem solved. But wait, that’s ugly and smells of something wrong. I should be able to just pass the object to the category_url
and have it return the correct value. What I did next was go down a rat hole trying to figure out what the named route was sending to the object to get the id. I had assumed id
, but in fact it’s to_param
, which I had already stubbed out as follows:
@category = mock_model(Category, :to_param => 1)
So what’s the problem? It turns out that to_param
must return a string. Makes sense. I changed it to the following and everything worked perfectly:
@category = mock_model(Category, :to_param => "1")
It’s little things like this that make learning so much fun. This issue is really indicative of a much bigger problem—my lack of understanding mocks and stubs. But, I’ll have more to write about this later.