如何在 rails 的测试中用 proc 和 yield 覆盖 class?

How can I overriding class with proc and yield in test on rails?

我有以下classes(仅作为示例),

class Background
  def self.add_thread(&blcok)
    Thread.new do
      yield
      ActiveRecord::Base.connection.close
    end
  end
end

class Email
  def send_email_in_other_thread
    Background.add_thread do
      send_email
    end
  end
  def send_email
    UserMailer.greeting_email.deliver_now
  end
end

以下代码用于测试,

class EmailTest < ActiveSupport::TestCase
  class Background
    def self.add_thread(&block)
      yield
    end
  end

  test 'should send email' do
    assert_difference 'ActionMailer::Base.deliveries.size', 1 do
      send_email_in_other_thread
    end
  end
end

但是这个测试失败了,"ActionMailer::Base.deliveries.size"没有变1。 大约 20 次中有 1 次成功。
我认为是因为修改了背景class。也许在测试中重写不起作用或者 yield proc 不是立即执行而是延迟执行。
我试过 'block.call' 而不是 yield,但结果是一样的。
我怎样才能让这个测试总是成功?

这看起来像是典型的竞争条件。 Thread.new returns 一旦线程被生成,而不是在它的工作完成时。

因为您的主线程不会停止执行,所以大多数时候您的断言是 运行 在邮件发送之前。

您可以使用 join 方法等待发送线程在返回之前完成执行,但它本质上又等同于单个线程,因为它会阻塞调用(主)线程直到工作完成。

Thread.new do
  yield
  ActiveRecord::Base.connection.close
end.join

然而,在 Rails 中已经有一些很棒的处理后台作业的方法。例如查看 SideKiq and Resque