This technique works well. I'm able to reimplement save (or save!) to simulate pretty much any race condition I want to handle. I ended up with something like this:
# mock save to simulate another user getting there first. sneaky_user = users(:brian) ItemForSale.send(:define_method, :save) do buyer = sneaky_user raise ActiveRecord::StaleObjectError, "Boom!" end item = itemsforsale(:vase) assert !item.sell_to(users(:allen)) # uses save internally assert item.errors.invalid?(:buyer) # models aren't reloaded, so I need to clean up ItemForSale.send(:undef_method, :save) Which should work well as a test for this: def sell_to(user) buyer = user begin save rescue ActiveRecord::StaleObjectError errors.add(:buyer, "already assigned for this item") return false end end On Jul 12, 9:02 pm, Brian <butler.bria...@gmail.com> wrote: > Ah, great idea. So in the absence of a mock object framework*, is > something like the following fairly standard? > > MyModel.send(:define_method, :save!) { raise > ActiveRecord::StaleObjectError, "Boom!" } > ... #test handling of race condition here > MyModel.send(:undef, :save!) > > Since "save!" is inherited, I don't think I need to worry about > aliasing the old one out of the way or anything. > > On Jul 12, 8:38 pm, Frederick Cheung <frederick.che...@gmail.com> > wrote: > > > > > On Jul 13, 1:04 am, Brian <butler.bria...@gmail.com> wrote: > > > > This is a two part question. Which type of locking should I use > > > (optimistic vs. pessimistic) and then how do I account for locking in > > > my tests? > > > > My scenario is essentially the purchase of a unique item where the > > > first person to click "Buy" gets the item and everyone else is out of > > > luck. As a single transaction, I need to determine whether the item > > > has already been purchased; if so provide an error message, otherwise > > > buy it. If I did not lock, race conditions may lead multiple people > > > to think they were succesful (and it would make a mess out of my > > > otherwise simple accounting). > > > > I'm leaning towards optimistic because: 1) my rails app is the only > > > thing touching the DB, 2) I'm trying to remain agnostic on DB software > > > for now, but 3) I don't really want a read-lock. Am I overlooking > > > anything that might push me in the other direction? > > > > For testing, I'm trying to be a good TDD citizen, so exactly what test > > > would cause me to write the logic to handle > > > ActiveRecord::StaleObjectError? (Or if I go with pessimistic, > > > the :lock => true option) Considering the record is located, updated, > > > and saved by the same method I can't think of a good way to actually > > > cause the race condition for a test. But common sense tells me it > > > will happen frequently once I have multiple users interacting with the > > > app. > > > for optimistic locking, > > > It can be quite handy to just mock save! and have it throw > > StaleObjectError. depending on your code you may also be able to do > > > firstInstance = SomeModel.find 1 > > secondInstance = SomeModel.find 1 > > secondInstance.created_at_will_change! #or anything that will make the > > save not be a no-op > > secondInstance.save! > > > #do something with firstInstance (it is now stale) > > > Pessimistic locking is a little different - not much will change for > > your code except that a select or a write may just block for a little > > longer than usual. You should be prepared to handle whatever your > > database does if it times out getting a lock or detects a deadlock. > > > Fred- Hide quoted text - > > > - Show quoted text -- Hide quoted text - > > - Show quoted text - --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk@googlegroups.com To unsubscribe from this group, send email to rubyonrails-talk+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---