运行下面列出的规范时出错。它不会等待线程完成并取消存储迁移方法,从而导致其中一个线程遇到真正的方法 注意到只有在加载了rails时才会发生这种情况,如果没有它们,它可以正常工作或只是更快完成......
规格:
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
提取的代码:
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
答案 0 :(得分:3)
你总是可以模拟Thread类来简单地不启动线程,而是运行代码,我发现它比你建议的方式重写测试要少得多。
将此添加到您的测试类:
describe MyClass do
before(:each) do
allow(Thread).to receive(:new).and_yield
end
#Your normal unaltered tests under here
end
这种方法的缺陷在于您不会遇到竞争条件,因此代码仍然可能包含错误,例如&#34;线程a和线程b同时写入同一个变量,导致问题&# 34 ;.您还应该考虑死锁方案,因为依赖其他线程完成某些操作的代码很可能会锁定这种方法。
答案 1 :(得分:1)
通过添加ensure块并调用threads.each(&amp;:join)并在records.each
之后移动recursion_preventer来解决