Wednesday, October 7, 2009

Testing/Implementing a Controller Method that Uses collection_select

Passing collection_select params via POST in a test and accessing those params can be a little bit tricky in Rails.

In the post (from the test) you must specify something like:

:parent_object_name => {:parent_attribute_object_id => some_id}
and then have to get it in the controller with:
params[:parent_object_name][:parent_attribute_object_id]

Let's say we have a model called Car that has a TireType attribute, and we want to use a collection_select (a select/dropdown box) to change it.

In the view, we create a select box with an associated button to make the change. Just for kicks, we also add a confirmation dialog:

    <% form_for TireType.new, :url => { :action => 'change_tire_type'} do |f| %>
      <%= collection_select(:car, :tire_type_id, TireType.all, :id, :name, {:prompt => true}) %>
      <%= submit_tag "Change Tire Type", :onclick => "return #{confirm_javascript_function('Are you sure you want to change the car\'s tire type?')}" %>
    <% end %>  

In the controller test (using test-spec just for this example):

context "anyone", ActionController::TestCase do
  use_controller CarController

...
  
  specify "should be able to change tire type" do
    new_tire_type = TireType.create!
    post :change_tire_type, :car_id => @car.id, :car => {:tire_type_id => new_tire_type.id}
    Car.find(@car.id).tire_type.id.should == new_tire_type.id
  end
end

And here is the method in the controller:

  def change_tire_type
    car = Car.find params[:car_id]
    tire_type_id = params[:car][:tire_type_id]
    new_tire_type = TireType.find(tire_type_id) 
    car.tire_type = new_tire_type
    car.save!
    redirect_to :action => :index
  end

No comments: