Re: How to spec transaction in Controller?

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 :slight_smile:

Andrea

Von: “David C.” [email protected]
Gesendet: 25.09.09 11:58:59
An: rspec-users [email protected]
Betreff: Re: [rspec-users] How to spec transaction in Controller ?

On Fri, Sep 25, 2009 at 3:06 AM, Andrea J. [email protected] 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.

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.

end
should_require_login :put, :delete
@planning.stub!(:destroy)
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

it “should flash the notice” do
context “and the Planning fails to delete” do
end


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

Neu: WEB.DE Doppel-FLAT mit Internet-Flatrate + Telefon-Flatrate
für nur 19,99 Euro/mtl.!* WEB.DE Produkte Übersicht: Apps, Browser, MailCheck & Co.
[WEB.DE Produkte Übersicht: Apps, Browser, MailCheck & Co.]