[rspec-users] How to write a correct RSpec for Polymorphic Association
David Chelimsky
dchelimsky at gmail.com
Sat Oct 17 13:24:51 EDT 2009
On Fri, Oct 16, 2009 at 7:21 AM, Sergey Rogachev <lists at ruby-forum.com> wrote:
> I don't know what is need to set in the variable ADDRESSES:
> Organization.stub!(:find_by_id).with("100").and_return(mock_organization(:id
> => "100", :addresses => ??? ))
>
> Somebody help me. Any ideas? I tried to find the solution of problem but
> I can't.
>
> --- rspec - controllers
> def mock_organization(stubs={})
> @parent_object ||= mock_model(Organization, stubs)
> end
>
> it "assigns a newly created address as @address" do
> Organization.stub!(:find_by_id).with("100").and_return(
> mock_organization(:id => "100", :addresses => ??? )) <<!--- here
> @parent_object.stub(:addresses).stub(:new).with({:these => 'params'})
>
> post :create, :address => {:these => 'params'}, :organization_id =>
> @parent_object.id
> assigns[:address].should equal(mock_address)
> end
>
> ---- models ----
> class Organization < ActiveRecord::Base
> has_many :addresses, :as => :addressable
> end
>
> class Address < ActiveRecord::Base
> belongs_to :addressable, :polymorphic => true
> end
>
> ----- controllers -----
> class AddressesController < ApplicationController
> before_filter :load_parent_object
>
> def create
> @address = @parent_object.addresses.new(params[:address])
>
> respond_to do |format|
> if @address.save
> flash[:notice] = 'Address was successfully created.'
> format.html { redirect_to(@parent_object) }
> format.xml { render :xml => @address, :status => :created,
> :location => @address }
> else
> format.html { render :action => "new" }
> format.xml { render :xml => @address.errors, :status =>
> :unprocessable_entity }
> end
> end
> end
>
> private
>
> def load_parent_object
> if params[:organization_id]
> @parent_object = Organization.find_by_id(params[:organization_id])
> end
> end
>
> end
Hi Sergey,
This is a great example of why it's helpful to write the examples
first. There is no need for this controller to know anything about
organizations - that can be handled by the model, where the
association is defined. Here's how I might have done this:
it "assigns a newly created address as @address" do
address = stub_model(Address)
Address.stub(:new).
with({:these => 'params'}).
and_return(address)
post :create, :address => {:these => 'params'}
assigns[:address].should equal(address)
end
class AddressesController < ApplicationController
def create
@address = Address.new(params[:address])
respond_to do |format|
if @address.save
flash[:notice] = 'Address was successfully created.'
format.html { redirect_to(@address.addressable) }
format.xml { render :xml => @address, :status => :created,
:location => @address }
else
format.html { render :action => "new" }
format.xml { render :xml => @address.errors, :status =>
:unprocessable_entity }
end
end
end
end
Now the post to the create action can include :organization_id within
params[:address] instead of as a separate hash key, and all is well.
That said, the following _should_ work w/ your current design:
it "assigns a newly created address as @address" do
address = mock('address')
addresses = mock('addresses')
addressable = mock_model(Organization, :id => "100", :addresses => addresses)
Organization.stub!(:find_by_id).with("100").and_return(addressable)
addressable.stub(:addresses).and_return(addresses)
addresses.stub(:new).with({:these => 'params'}).and_return(address)
post :create, :address => {:these => 'params'}, :organization_id => "100"
assigns[:address].should equal(address)
end
class AddressesController < ApplicationController
before_filter :load_addressable
def create
@address = @addressable.addresses.new(params[:address])
respond_to do |format|
if @address.save
flash[:notice] = 'Address was successfully created.'
format.html { redirect_to(@addressable) }
format.xml { render :xml => @address, :status => :created,
:location => @address }
else
format.html { render :action => "new" }
format.xml { render :xml => @address.errors, :status =>
:unprocessable_entity }
end
end
end
private
def load_addressable
if params[:organization_id]
@addressable = Organization.find_by_id(params[:organization_id])
end
end
end
Notes:
* I changed @parent_object to @addressable, since that's how the
association is labeled in the code.
* The only object in the code example that uses mock_model is the one
that is actually needs to behave like an AR model in the context of
this example
Let me know if you have any questions.
Cheers,
David
More information about the rspec-users
mailing list