Hello David,

thank you very much for your very detailed answer !!

I'll act on your advice and remove the transaction logic from the controller.
So that the controller has to call only one method, which is defined in the 
Model 
and cares about the transaction.

You're right, this is an example, that writing specs helps to develop better 
code :)

Andrea


*Von:* "David Chelimsky" <dchelim...@gmail.com>
*Gesendet:* 25.09.09 11:58:59
*An:* rspec-users <RSPEC-USERS@RUBYFORGE.ORG>
*Betreff:* Re: [rspec-users] How to spec transaction in Controller ?



On Fri, Sep 25, 2009 at 3:06 AM, Andrea Jahn <anja-ema...@web.de> wrote:
> Hi,
>
> in my Controller I have some tansactions. I'm trying to write specs, which 
> test the two program pathes (an exception is thrown or no exception is 
> thrown). But the test seems always to go into the path, where the exception 
> is catched.
>
> Here's an example for the delete action
> (destroy method is overwritten, so :dependent => :destroy is not possible 
> here)

Off topic, but ActiveRecord offers a number of ways to interact with
model and transaction life cycles. The fact that you are overriding
destroy rather than hooking into the destroy cycle means you're
bypassing a lot of functionality that you now have to implement
yourself (hence the problem you are describing in this post). If there
is any way for you to use those hooks instead, I'd strongly recommend
it.

>
> The test case "should flash the notice" fails, because not the program path 
> of success is executed, but the rescue path is executed.
>
> Perhaps my exception handling is generally wrong ?
>
> Thanks
> Andrea
>
> plannings_controller.rb
> -----------------------
>
> def destroy
> @planning = Physical::Planning::Planning.find(params[:id])
>
> respond_to do |format|
> begin
> ActiveRecord::Base.transaction do
> @planning.destroy
> @planning.planned_amounts.each { |planned_amount| planned_amount.destroy }
> end

Even if you've overridden destroy, why not just include this code in
the custom destroy method? Then the controller could just call
@planning.destroy and that method can worry about transactions.

> # success
> flash[:notice] = "The #{Physical::Planning::Planning::DISPLAY_NAME.downcase} 
> '" + @planning.title + "' was successfully deleted."
> format.html { redirec t_to(planning_plannings_url) }
> format.xml { head :ok }
> rescue => e
> # DB raised errors
> flash[:error] = "The #{Physical::Planning::Planning::DISPLAY_NAME.downcase} 
> '" + (@planning.nil? ? "" : @planning.title) + "' could not be deleted. 
> Error:" + e
> format.html { redirect_to(planning_plannings_url) }
> format.xml { render :xml => @planning.errors, :status => 
> :unprocessable_entity }
> end
> end
> end
>
>
>
> plannings_co ntroller_spec.rb
> ----------------------------
>
> describe Physical::Planning::PlanningsController, "DELETE" do
> include PlanningMacros
>
> should_require_login :put, :delete
> should_require_authorization :put, :delete
>
> context "when the user is logged in" do
> before(:each) do
> do_login
> do_authorization
>
> @planning = mock_model(Physical::Planning::Planning, 
> valid_planning_attributes)
> @planned_amount = mock_model(Physical::Planning::PlannedAmount, 
> valid_planned_amount_attributes)
>
> @planning.stub!(:destroy)
> @planning.stub!(:planned_amounts).and_return([...@planned_amount])
>
> Physical::Planning::Planning.stub!(:find).and_return(@planning)
>
> @plan ning.errors.stub!(:full_messages).and_return([])
> end
>
> # delete successfully
> # -------------------
>
> context "and the Planning deletes successfully" do
>
> ....
>
> it "should destroy the Physical::Planning::Planning in a transaction" do
> Physical::Planning::Planning.should_receive(:transaction).any_number_of_times.and_yield(
> @planning.should_receive(:destroy)
> )

This ^^ isn't doing anything. any_number_of_times includes 0, which is
the number of times Planning (the class object) receives transaction
(the controller calls ActiveRecord::Base.transaction). I'd recommend
leaving out any_number_of_times. By default, should_receive expects
exactly one call, which is the right number in this case. And it
should be on ActiveRecord::Base, given the current state of the
implementation.

This, however, is a great example of how writing examples first can
have a positive impact on design. If I were writing this, I never
would have specified transactions in a controller action. This example
would simply be:

it "should destroy the Physical::Planning::Planning in a transaction" do
@planning.should_receive(:destroy)
do_put_delete
end

Then the implementation would only need call @planning.destroy to pass
the example. The transactional nature of the destroy method would be
specified in the model spec, and now any other code that gets written
later that needs to destroy a Planning instance can just send it
destroy() and you won't have to duplicate this transaction code in
each of those cases.

HTH,
David

> do_put_delete
> end
>
> it "should destroy the PlannedAmounts of the Physical::Planning::Planning in 
> a transaction" do
> Physical::Planning::Planning.should_receive 
> (:transaction).any_number_of_times.and_yield(
> @planned_amount.should_receive(:destroy)
> )
> do_put_delete
> end
>
> it "should flash the notice" do
> do_put_delete
> flash[:notice].should match(/was successfully deleted/)
> end
>
> ...
>
> end
>
> # delete fails
> # ------------
> context "and the Planning fails to delete" do
>
> it "should flash error message because of database exception" do
> Physical::Planning::Planning.should_receive(:transaction).any_ 
> number_of_times.and_yield(
> @planning.should_receive(:destroy).and_raise(ActiveRecord::RecordInvalid.new(@planning))
> )
> do_put_delete
> flash[:error].should match(/could not be deleted/)
> end
> end
> end
> end
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users
        



        
Neu: WEB.DE Doppel-FLAT mit Internet-Flatrate + Telefon-Flatrate 
für nur 19,99 Euro/mtl.!* *http://produkte.web.de/go/02/* 
[http://produkte.web.de/go/02/]
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to