我试图在竞争条件下测试部分代码。我遇到的问题与唯一性验证有关,因为事实证明铁轨中的竞争条件并不安全。我相信我能够解决这个问题,但我不确定如何测试我的解决方案。
我最接近的是以下(灵感来自: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中不是线程安全的?
我的问题有更好的方法吗?正如我上面所说,我已经遇到了一些不同的问题,我认为这应该是一个足够普遍的问题,应该存在良好的测试标准。
答案 0 :(得分:1)
一些事情:
1)可能是我的无知,但true while should_wait
线对我来说似乎不对。更像while should_wait do
的东西似乎更像你想要的东西。你也可以打电话pod.save
这似乎没有意义,所以我猜这不完全是你正在使用的代码。
2)我希望数据库清理工作,因为我认为如果你使用"截断"策略将在测试运行时通过并截断每个表。我疯狂的猜测是你已经将它配置为仅运行集成测试,这是一个单元测试,或类似的东西。如果不是这样,请尝试在测试结束时调用DatabaseCleaner.truncate
(或者你明确地这样做),看看是否有效。
3)你能用数据库中的唯一索引解决问题吗?这完全取消了对此测试的需求,因为您只需信任您的数据库。当您获得非唯一值时,您可以在代码中以非验证方式处理它。速度也快得多,因为每次保存时都不必进行额外的SQL调用。
4)无法从给出的信息中了解您为什么会收到循环依赖问题。我以前遇到过这个问题,并在文件顶部做了一个puts caller
来尝试诊断。