2016-11-29 47 views
1

我試圖測試競爭條件的代碼部分。我遇到的問題與唯一性驗證有關,事實證明,這種驗證在鐵軌中的競爭條件下是不安全的。我相信我可以解決這個問題,但我不知道如何測試我的解決方案。測試競爭條件的軌道,然後清理

我來最接近的是以下(靈感: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,並嘗試了所有不同的策略。另外,有時候我會得到一個關於常量鍵的循環依賴問題。不知道爲什麼,但我猜測它的原因要求不要在Ruby中保持線程安全?

有沒有更好的辦法來解決我的問題?正如我上面指出的那樣,我已經得到了一些不同的問題,我認爲應該存在一個普遍的問題,那就是良好的測試標準應該存在。

回答

1

有幾件事情:

1)可能是我的無知,但true while should_wait線看來我錯了。更像while should_wait do似乎更像你想要的。你也打電話pod.save這似乎沒有道理,所以我猜這不是你正在使用的代碼。

2)我希望數據庫清理工作,因爲我認爲如果你使用「截斷」策略,它會通過並在測試運行時截斷每個表。我的野驢猜測是你已經配置它只運行集成測試,這是一個單元測試,或類似的東西。如果不是這樣,請在測試結束時嘗試呼叫DatabaseCleaner.truncate(或者您明確地這樣做),看看是否有效。

3)你可以通過數據庫中的唯一索引解決問題嗎?這完全不需要這個測試,因爲你只需要信任你的數據庫。當你得到一個非唯一值時,你可以在你的代碼中以非驗證的方式處理它。速度也快得多,因爲您不必在每次保存時進行額外的sql調用。

4)不可能從給出的信息中知道爲什麼你會得到循環依賴問題。我之前遇到過這個問題,並在文件頂部做了puts caller以嘗試進行診斷。

+0

1)循環只是一個醜陋的黑客,以確保所有線程同時開始。你對「pod.save」是正確的,我編輯它以匹配我的例子。 2)我必須嘗試一下。我想我在每次測試後都將其配置爲清潔,但您很可能是正確的。 3)是的,這是我使用的解決方案。你是對的,我可以信任數據庫來正確處理它。我還有其他一些可能難以解決的情況,所以測試競態條件的一般方法仍然很棒。 –