Reputation: 1501
There is an error running a spec listed below. It does not wait for threads to complete and unstub a migrate method resulting that one of threads hits a real method.
Noticed it only happens with rails loaded, without them it is working correctly or just finish faster...
Spec:
it "should not allow infinit recursion" do
runner.stub(total_records: 4)
runner.stub(:fetch_records_batch).and_return([:one, :two])
runner.should_receive(:migrate).at_most(100).times
expect { runner.run }.to raise_error(Exception, /Migration fall into recursion/)
end
it "should pass"
1.should eq 1
end
Extracted piece of code:
class Runner
def migrate(record)
raise NotImplementedError
end
def run
while have_records?(records = fetch_records_batch)
threads = []
records.each do |record|
threads << Thread.new(record) do |thread_record|
begin
result = migrate(thread_record)
rescue RuntimeError => exception
register_event :record_migration_error, thread_record, exception
end
end
recursion_preventer
end
threads.each(&:join)
end
end
def recursion_preventer
@counter ||= 0
@counter += 1
raise Exception, "Migration fall into recursion. Check logs." if @counter > (total_records * 1.1).round + 10
end
end
Upvotes: 3
Views: 3062
Reputation: 12916
You can always mock the Thread class to simply not start threads, but run the code instead, which I find is much less intrusive than rewriting tests the way you suggest.
Add this to your test-class:
describe MyClass do
before(:each) do
allow(Thread).to receive(:new).and_yield
end
#Your normal unaltered tests under here
end
The pitfall of this method is that you won't experience race conditions so the code can still include bugs such as "thread a and thread b writes to the same variable at the same time, causing issues". You should also consider dead lock scenarios, as code that relies on other threads to finish something will most likely lock up with this approach.
Upvotes: 5
Reputation: 1501
solved by adding ensure block and call threads.each(&:join) and moving recursion_preventer just after records.each
Upvotes: 1