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 <[email protected]> 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 <[email protected]>
> wrote:
>
>
>
> > On Jul 13, 1:04 am, Brian <[email protected]> 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 [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---