Skip to content

Instantly share code, notes, and snippets.

@a-leung
Last active January 28, 2016 20:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a-leung/64f8942367b6c3566500 to your computer and use it in GitHub Desktop.
Save a-leung/64f8942367b6c3566500 to your computer and use it in GitHub Desktop.
rspec and ActiveRecord::Base.transaction testing
def set_new_name_on_items?
success = ActiveRecord::Base.transaction do
model.items.each do |i|
i.name = 'new name'
i.save or raise ActiveRecord::Rollback
end
!!success
end
it 'returns false on a single failing transaction' do
items = [ item_a, item_b ]
model = Fabricate(:model, items: items)
item_b.stub(:save!).and_return(false)
expect(model.set_new_name_on_items).to be false
end
it 'returns true on all save transaction' do
items = [ item_a, item_b ]
model = Fabricate(:model, items: items)
expect(model.set_new_name_on_items).to be true
end
@a-leung
Copy link
Author

a-leung commented Jan 20, 2016

the spec was not passing since I stubbed item_b with: and_return(false). To get the transaction block to fail, the stub must be: and_raise(Error)

@a-leung
Copy link
Author

a-leung commented Jan 21, 2016

found a way to make the test work with and_return(false).

The important thing is to have raise ActiveRecord::Rollback within the ActiveRecord::Base.transaction so the every object in the transaction block is rolled back if any one of them errors. The previous example using save! worked, but if one transaction in the block saved successfully, it would not rollback if another failed.

@a-leung
Copy link
Author

a-leung commented Jan 28, 2016

actually, best way to stub this is: .stub(:create_or_update):
From the docs:

# File activerecord/lib/active_record/base.rb, line 2575
      def save
        create_or_update
      end

# File activerecord/lib/active_record/base.rb, line 2592
      def save!
        create_or_update || raise(RecordNotSaved)
      end

so, save and save! are slightly different but both call create_or_update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment