测试轨道的竞争条件,然后进行清理

时间:2016-11-29 18:21:08

标签: ruby-on-rails ruby multithreading race-condition minitest

我试图在竞争条件下测试部分代码。我遇到的问题与唯一性验证有关,因为事实证明铁轨中的竞争条件并不安全。我相信我能够解决这个问题,但我不确定如何测试我的解决方案。

我最接近的是以下(灵感来自:http://blog.arkency.com/2015/09/testing-race-conditions/):

test "Can't create duplicate keys with same value and keyboard" do
  assert_equal(5, ActiveRecord::Base.connection.pool.size)
  begin
    concurrency_level = 4
    keyboard = create :keyboard
    should_wait = true

    statuses = {}

    threads = Array.new(concurrency_level) do |i|
      Thread.new do
        true while should_wait
        begin
          # Unique validation for key values exists scoped to keyboard
          key = keyboard.keys.new(value: 'a')
          statuses[i] = key.save
        rescue ActiveRecord::RecordNotUnique
          statuses[i] = false
        end
      end
    end
    should_wait = false
    threads.each(&:join)

    assert_equal(1, keyboard.keys.count)
    assert_equal(1, statuses.count { |_k, v| v })
    assert_equal(3, statuses.count { |_k, v| !v })
  ensure
    ActiveRecord::Base.connection_pool.disconnect!
  end
end

上面的代码结构与我的完全相同,但模型已经变得更加通用。

测试本身似乎没问题。但是,之后不会删除测试中创建的密钥。我正在使用DatabaseCleaner,我尝试过所有不同的策略。此外,有时我会为常量Key获得循环依赖性问题。不知道为什么,但是我猜测它是否需要在ruby中不是线程安全的?

我的问题有更好的方法吗?正如我上面所说,我已经遇到了一些不同的问题,我认为这应该是一个足够普遍的问题,应该存在良好的测试标准。

1 个答案:

答案 0 :(得分:1)

一些事情:

1)可能是我的无知,但true while should_wait线对我来说似乎不对。更像while should_wait do的东西似乎更像你想要的东西。你也可以打电话pod.save这似乎没有意义,所以我猜这不完全是你正在使用的代码。

2)我希望数据库清理工作,因为我认为如果你使用"截断"策略将在测试运行时通过并截断每个表。我疯狂的猜测是你已经将它配置为仅运行集成测试,这是一个单元测试,或类似的东西。如果不是这样,请尝试在测试结束时调用DatabaseCleaner.truncate(或者你明确地这样做),看看是否有效。

3)你能用数据库中的唯一索引解决问题吗?这完全取消了对此测试的需求,因为您只需信任您的数据库。当您获得非唯一值时,您可以在代码中以非验证方式处理它。速度也快得多,因为每次保存时都不必进行额外的SQL调用。

4)无法从给出的信息中了解您为什么会收到循环依赖问题。我以前遇到过这个问题,并在文件顶部做了一个puts caller来尝试诊断。