Rspec应该等待线程完成

时间:2013-04-19 09:59:56

标签: ruby-on-rails ruby multithreading rspec

运行下面列出的规范时出错。它不会等待线程完成并取消存储迁移方法,从而导致其中一个线程遇到真正的方法 注意到只有在加载了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

2 个答案:

答案 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来解决