你如何使用ActiveRecord测试Postgres的LISTEN / NOTIFY?

时间:2014-09-12 21:28:46

标签: ruby postgresql activerecord rspec

假设我正在使用pg gem和RSpec,我应该采取什么方法来正确测试我的LISTENNOTIFY语句是否有效? pg的wait_for_notify块,所以看起来我不能“通知,然后听”,或“听,然后通知”。我忽略了什么吗?

例如:

it "notifies" do
  conn = ActiveRecord::Base.connection

  it_ran = false

  conn.execute "LISTEN my_channel"
  conn.execute "NOTIFY my_channel, 'hello'"
  conn.wait_for_notify(1) do |channel, pid, payload|
    it_ran = true
  end

  expect(it_ran).to eq true
end

修改

这适用于控制器,甚至是rails控制台,但由于某种原因,它在RSpec测试中不起作用。奇怪的是,直接使用pg gem确实有效。为什么ActiveRecord可能无法在这种情况下工作?

2 个答案:

答案 0 :(得分:1)

wait_for_notify()仅在需要时阻止。也就是说,当通知队列中还没有东西时。

在您的代码中,队列中已经存在基于您的第一个NOTIFY的通知,因此wait_for_notify()将立即返回并且将设置it_ran。

如果我撕掉ActiveRecord的东西并直接使用pg,这正是发生的事情。

答案 1 :(得分:0)

原来问题在于DatabaseCleaner。有了这个,我尝试使用其他策略而不是交易,但没有任何效果;似乎让LISTEN / NOTIFY处理ActiveRecord连接的唯一方法是禁用它进行一次测试。

以下是我最终为特定测试禁用DatabaseCleaner的方法。在我的支持配置文件中:

RSpec.configure do |config|
  # BEFORE:
  # config.before(:each) do
  #   DatabaseCleaner.strategy = :transaction
  # end

  # AFTER:
  config.before(:each) do |test|
    unless test.metadata[:no_database_cleaner]
      DatabaseCleaner.strategy = :transaction
    end
  end

end

在我的spec文件中:

RSpec.describe "Postgres LISTEN / NOTIFY" do
  it "notifies", :no_database_cleaner => true do
    # [clipped]
  end
end

现在,只要我需要测试LISTEN / NOTIFY,我就会将:no_database_cleaner => true添加到it块。